[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non:\n  push:\n    branches:\n      - master-11\n  pull_request:\n    types: [opened, synchronize, reopened]\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    if: startsWith(github.event.head_commit.message, '🏁 Releasing version') != true\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis\n      - name: Set up JDK 21\n        uses: actions/setup-java@v4\n        with:\n          java-version: 21\n          distribution: 'zulu'\n      - name: Cache Maven packages\n        uses: actions/cache@v4\n        with:\n          path: ~/.m2\n          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}\n          restore-keys: ${{ runner.os }}-m2\n      - name: Build and analyze\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n        run: ./mvnw -B verify"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master-11\" ]\n  pull_request:\n    branches: [ \"master-11\" ]\n  schedule:\n    - cron: \"46 7 * * 1\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ java ]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v2\n        with:\n          languages: ${{ matrix.language }}\n          queries: +security-and-quality\n\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v2\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v2\n        with:\n          category: \"/language:${{ matrix.language }}\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Release version\"\n        required: true\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0\n\n      - name: 'Set up Java'\n        uses: actions/setup-java@v4\n        with:\n          java-version: 21\n          distribution: 'zulu'\n          server-id: central\n          server-username: MAVEN_USERNAME\n          server-password: MAVEN_CENTRAL_TOKEN\n          gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}\n          gpg-passphrase: MAVEN_GPG_PASSPHRASE\n\n      - name: 'Cache Maven packages'\n        uses: actions/cache@v4\n        with:\n          path: ~/.m2\n          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}\n          restore-keys: ${{ runner.os }}-m2\n\n      - name: Update version\n        id: version\n        run: |\n          VERSION=${{ github.event.inputs.version }}\n          echo \"Updating POMs to version $VERSION\"\n          ./mvnw -B versions:set versions:commit -DnewVersion=$VERSION\n          git config --global user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"GitHub Action\"\n          git commit -a -m \"🏁 Releasing version $VERSION\"\n          git push origin HEAD:master-11\n\n      - name: Release to Maven Central\n        env:\n          MAVEN_USERNAME: ${{ secrets.PUBLISHER_PORTAL_USERNAME }}\n          MAVEN_CENTRAL_TOKEN: ${{ secrets.PUBLISHER_PORTAL_TOKEN }}\n          MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}\n        run: |\n          export GPG_TTY=$(tty)\n          ./mvnw --no-transfer-progress -B --file pom.xml \\\n            -Drepository.url=https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git \\\n            -Dmaven.site.skip=true -Drelease=true deploy -s ./settings.xml\n\n      - name: Release to GitHub\n        env:\n          JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: ./mvnw -B --file pom.xml -Prelease -pl :calendar jreleaser:full-release\n\n      - name: JReleaser output\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: jreleaser-logs\n          path: |\n            target/jreleaser/trace.log\n            target/jreleaser/output.properties\n"
  },
  {
    "path": ".gitignore",
    "content": "/target/\n.idea\n*.iml\n.project\n*.prefs\nCalendarFXApp/.classpath\n*.classpath\n*.log\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n# \n#   http://www.apache.org/licenses/LICENSE-2.0\n# \n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip\nwrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## [11.8.3](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.8.3) (2019-10-23)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.8.2...11.8.3)\n\n**Closed issues:**\n\n- Missing icons [\\#57](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/57)\n- Adapt iCal4j recurrence [\\#50](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/50)\n\n**Merged pull requests:**\n\n- Fix StringIndexOutOfBoundsException [\\#63](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/63) ([saarmbruster](https://github.com/saarmbruster))\n- Fix page without transition [\\#61](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/61) ([saarmbruster](https://github.com/saarmbruster))\n- Improve start-up performance [\\#60](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/60) ([saarmbruster](https://github.com/saarmbruster))\n\n## [11.8.2](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.8.2) (2019-10-08)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.8.1...11.8.2)\n\n**Closed issues:**\n\n- Merge updates from master into master-11 [\\#59](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/59)\n\n## [11.8.1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.8.1) (2019-10-01)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.8.0...11.8.1)\n\n## [11.8.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.8.0) (2019-09-27)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.7.0...11.8.0)\n\n**Merged pull requests:**\n\n- Add missing German translation [\\#58](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/58) ([saarmbruster](https://github.com/saarmbruster))\n- Fix typo in private method name [\\#56](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/56) ([saarmbruster](https://github.com/saarmbruster))\n- Add listener to dayPageLayout [\\#55](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/55) ([saarmbruster](https://github.com/saarmbruster))\n- iCal4j recurrence [\\#51](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/51) ([mwkroening](https://github.com/mwkroening))\n\n## [11.7.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.7.0) (2019-09-25)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.6.4...11.7.0)\n\n## [11.6.4](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.6.4) (2019-09-01)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.6.3...11.6.4)\n\n## [11.6.3](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.6.3) (2019-08-28)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/8.6.1...11.6.3)\n\n## [8.6.1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/8.6.1) (2019-08-28)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/8.6.0...8.6.1)\n\n## [8.6.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/8.6.0) (2019-08-28)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.6.2...8.6.0)\n\n## [11.6.2](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.6.2) (2019-08-28)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/8.5.1...11.6.2)\n\n## [8.5.1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/8.5.1) (2019-08-28)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.6.1...8.5.1)\n\n## [11.6.1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.6.1) (2019-08-19)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/11.6.0...11.6.1)\n\n## [11.6.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/11.6.0) (2019-08-19)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/v11.5.0...11.6.0)\n\n**Implemented enhancements:**\n\n- Add Continuous Delivery to Maven Central [\\#54](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/54) ([martinfrancois](https://github.com/martinfrancois))\n\n**Closed issues:**\n\n- Using CalendarFx 11.5.0 with openJFx 12 facing problem of Module not found [\\#47](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/47)\n- 11.5.0 Unable to change event behavior  [\\#46](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/46)\n- Save Entries into database [\\#45](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/45)\n- Issues figuring out scene builder [\\#43](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/43)\n- \\[11.5.0\\] Remove snapshot dependency on controlsfx 9.0.1-SNAPSHOT once released [\\#41](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/41)\n- Data Save [\\#40](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/40)\n- JavaDocs Errors [\\#31](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/31)\n- Memory-Leak in DateControl/PopOver [\\#29](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/29)\n- Drag and drop functionality [\\#28](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/28)\n\n**Merged pull requests:**\n\n- Replace Joda-Time with java.time in CalendarFXView [\\#49](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/49) ([mwkroening](https://github.com/mwkroening))\n- Upgrade dependencies [\\#48](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/48) ([mwkroening](https://github.com/mwkroening))\n\n## [v11.5.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/v11.5.0) (2018-09-15)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/v8.5.0...v11.5.0)\n\n**Closed issues:**\n\n- Day view multiple selection not working [\\#39](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/39)\n-  Could not find artifact com.calendarfx:google:jar:8.5.0  [\\#36](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/36)\n- Data Save and Google [\\#35](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/35)\n- Miscount of Seconds [\\#32](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/32)\n- Calendar is not visible [\\#22](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/22)\n- Create a Java 10 branch and release. [\\#14](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/14)\n\n**Merged pull requests:**\n\n- Fixed issues regarding the Drag and Drop of the entry at the start and end of it [\\#37](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/37) ([arias9306](https://github.com/arias9306))\n- pt\\_BR messages [\\#34](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/34) ([GabrielZulian](https://github.com/GabrielZulian))\n\n## [v8.5.0](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/v8.5.0) (2018-08-08)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/v8.4.2-maven-central...v8.5.0)\n\n**Closed issues:**\n\n- Disable drag and drop Entry [\\#30](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/30)\n- calendarfx.com [\\#27](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/27)\n- Use CalendarFX in Scene Builder [\\#25](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/25)\n- Tests fails when building 8.4.1 [\\#17](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/17)\n- Publish artefacts to Maven central. [\\#12](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/12)\n\n**Merged pull requests:**\n\n- Enhancement on GUI, user/development experience and bugfixing [\\#33](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/33) ([arias9306](https://github.com/arias9306))\n\n## [v8.4.2-maven-central](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/v8.4.2-maven-central) (2018-01-24)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/v8.4.2...v8.4.2-maven-central)\n\n**Closed issues:**\n\n- Issue with maven 8.4.1 and 8.4.2 [\\#21](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/21)\n- Maven repository failing [\\#20](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/20)\n- Update to latest ControlsFX and remove CustomMasterDetailPane [\\#13](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/13)\n\n## [v8.4.2](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/v8.4.2) (2018-01-10)\n[Full Changelog](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/compare/v8.4.1...v8.4.2)\n\n**Closed issues:**\n\n- Can't create CalendarView in own Project [\\#16](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/16)\n\n**Merged pull requests:**\n\n- fixes a bug and make DayEntryViewSkin more reusable [\\#19](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/19) ([teymourlouie](https://github.com/teymourlouie))\n- DetailedWeekView adjustToFirstDayOfWeek and Entry equation [\\#18](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/18) ([imario42](https://github.com/imario42))\n- Update ControlsFx version [\\#15](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/15) ([sanaehirotaka](https://github.com/sanaehirotaka))\n\n## [v8.4.1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/tree/v8.4.1) (2017-10-23)\n**Closed issues:**\n\n- Reusability of DayEntryViewSkin [\\#9](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/issues/9)\n\n**Merged pull requests:**\n\n- optimized imports to single line imports [\\#11](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/11) ([imario42](https://github.com/imario42))\n- reformat and copyright header. no code changes. [\\#10](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/10) ([imario42](https://github.com/imario42))\n- additional drag/drop enhancements [\\#8](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/8) ([imario42](https://github.com/imario42))\n- drag/drop issues [\\#7](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/7) ([imario42](https://github.com/imario42))\n- immediately update UI if just the calendar of an entry changes [\\#6](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/6) ([imario42](https://github.com/imario42))\n- renamed CalendarFXView to CalendarFXExperimental [\\#4](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/4) ([imario42](https://github.com/imario42))\n- removed invalid relative parent path [\\#3](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/3) ([imario42](https://github.com/imario42))\n- Create LICENSE [\\#2](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/2) ([dlemmermann](https://github.com/dlemmermann))\n- Create CODE\\_OF\\_CONDUCT.md [\\#1](https://github.com/dlsc-software-consulting-gmbh/CalendarFX/pull/1) ([dlemmermann](https://github.com/dlemmermann))\n\n\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
  },
  {
    "path": "CHANGES.txt",
    "content": "-------------------------------------------------------------------------------\nRELEASE NOTES, VERSION 11.12.x (October 2022)\n-------------------------------------------------------------------------------\n\n*** NEW FEATURES\n\nDayView / WeekDayView / WeekView\n--------------------------------\n\nIt is now possible to create new entries via press and drag. It is also\npossible to configure whether new entries shall be created after a single or\na double click. We also changed the behaviour to automatically bring up the\npopover for newly created entries as this is what most people would expect.\nThis can be configured by calling DateControl.setShowDetailsUponEntryCreation.\n\nResourcesView\n-------------\n\nA new view called \"ResourcesView\" was added. It can be used to add allocations\nto one or more resources / people. A typical use case would be a personal that\nis working at a barber / hairdresser. The calendar entries would represent\ncustomer appointments. This view can either display \"dates over resources\" or\n\"resources over dates\". As part of the development of we added the \"Resource\"\nmodel class. This class stores a regular calendar and a special one called the\n\"availability calendar\". The availability of a resource can be edited when the\nview's \"editAvailability\" property is set to true.\n\nBackgroundCanvas (mainly private API)\n---------------------------------\n\nTo visualize the availability of a resource we decided to use a canvas instead\nof scene graph nodes. The performance is just much better and availability is\nusually expressed by greying out the background of a resource. For controlling\nthe availability granularity and color please see DateControl.availabilityGrid\nand DateControl#availabilityColor.\n\nThe background canvas also renders new light-weight grid lines. This is needed\nas the availability grid could lead to the creation of many more grid lines\ncompared to the previous full- and half-hour grid lines. However, these light-\nweight grid lines can not be styled via CSS. See the DateControl.gridLineColor\nproperty.\n\nDayEntryView\n------------\n\nIt is now possible to set a \"click count\" number for showing the details of an\nentry view. In the past the user always had to double-click on the entry. Now\na single or a tripple-click can also be configured.\n\nGoogle Map View\n---------------\n\nA new view class has been added that is capable of looking up a static map\nimage from Google based on a given address / location string. The class is\ncalled EntryMapView and is part of the EntryDetailsView. The map view will\nonly be visible if an address and a Google Maps API key exist.\n\n*** ENHANCEMENTS\n\nDayViewEditController\n---------------------\n\nWith the addition of the availability feature we had to revisit the logic for\nediting day entry views. The code became too messy and had to be refactored.\nWhen comparing the before and after of this class one will notice that the new\ncode is much more structured.\n\nCalendar\n--------\n\nA new \"user object\" property was added to the calendar so that a link can be\ncreated between the calendar and its data source or its business object.\n\nInterval\n--------\n\nA new \"with\" method has been added to derive a new interval from an existing\ninterval by passing in a new duration. Interval.withDuration(...).\n\n*** FIXES\n\nRecurrence\n----------\n\nRecurrence entries were not updated correctly in certain circumstances. This\nhas been fixed.\n\nMemory Leaks\n------------\n\nMemory leaks were recently introduced after we started using the ListProperty\nclass, e.g. for \"available time zones\" and for \"resources\". There seems to be\na known issue when applications try to \"unbind\" from a ListProperty. This seems\nto fail and objects in the collection are not being collected."
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dlemmermann@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CalendarFXApp/.gitignore",
    "content": "/target\n*.iml\n"
  },
  {
    "path": "CalendarFXApp/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>application</artifactId>\n    <name>CalendarFXApp</name>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.github.mkpaz</groupId>\n            <artifactId>atlantafx-base</artifactId>\n            <version>2.0.1</version>\n        </dependency>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>fr.brouillard.oss</groupId>\n            <artifactId>cssfx</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-controls</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-fxml</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-swing</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>net.raumzeitfalle.fx</groupId>\n            <artifactId>scenic-view</artifactId>\n            <version>11.0.2</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.openjfx</groupId>\n                    <artifactId>*</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.app.CalendarApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "CalendarFXApp/src/main/java/com/calendarfx/app/CalendarApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.app;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.CalendarView;\nimport fr.brouillard.oss.cssfx.CSSFX;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.scene.Scene;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\nimport org.scenicview.ScenicView;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.util.Objects;\n\npublic class CalendarApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        CalendarView calendarView = new CalendarView();\n        calendarView.setEnableTimeZoneSupport(true);\n\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        katja.setShortName(\"K\");\n        dirk.setShortName(\"D\");\n        philip.setShortName(\"P\");\n        jule.setShortName(\"J\");\n        armin.setShortName(\"A\");\n        birthdays.setShortName(\"B\");\n        holidays.setShortName(\"H\");\n\n        katja.setStyle(Style.STYLE1);\n        dirk.setStyle(Style.STYLE2);\n        philip.setStyle(Style.STYLE3);\n        jule.setStyle(Style.STYLE4);\n        armin.setStyle(Style.STYLE5);\n        birthdays.setStyle(Style.STYLE6);\n        holidays.setStyle(Style.STYLE7);\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, holidays, katja, dirk, philip, jule, armin);\n\n        calendarView.getCalendarSources().setAll(familyCalendarSource);\n        calendarView.setRequestedTime(LocalTime.now());\n\n        StackPane stackPane = new StackPane();\n        stackPane.getChildren().addAll(calendarView); // introPane);\n\n        Thread updateTimeThread = new Thread(\"Calendar: Update Time Thread\") {\n            @Override\n            public void run() {\n                while (true) {\n                    Platform.runLater(() -> {\n                        calendarView.setToday(LocalDate.now());\n                        calendarView.setTime(LocalTime.now());\n                    });\n\n                    try {\n                        // update every 10 seconds\n                        sleep(10000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n\n                }\n            }\n        };\n\n        updateTimeThread.setPriority(Thread.MIN_PRIORITY);\n        updateTimeThread.setDaemon(true);\n        updateTimeThread.start();\n\n        Scene scene = new Scene(stackPane);\n        if (Boolean.getBoolean(\"atlantafx\")) {\n            scene.getStylesheets().add(Objects.requireNonNull(CalendarView.class.getResource(\"atlantafx.css\")).toExternalForm());\n        }\n\n        scene.focusOwnerProperty().addListener(it -> System.out.println(\"focus owner: \" + scene.getFocusOwner()));\n        CSSFX.start(scene);\n\n        primaryStage.setTitle(\"Calendar\");\n        primaryStage.setScene(scene);\n        primaryStage.setWidth(1300);\n        primaryStage.setHeight(1000);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n\n//        ScenicView.show(scene);\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXApp/src/main/java/com/calendarfx/app/CalendarAppAtlantaFX.java",
    "content": "package com.calendarfx.app;\n\nimport atlantafx.base.theme.NordDark;\nimport atlantafx.base.theme.NordLight;\n\npublic class CalendarAppAtlantaFX extends CalendarApp {\n\n    public static void main(String[] args) {\n        System.setProperty(\"atlantafx\", \"true\");\n        setUserAgentStylesheet(new NordDark().getUserAgentStylesheet());\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXApp/src/main/java/com/calendarfx/app/CalendarAppLauncher.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.app;\n\npublic class CalendarAppLauncher {\n\n    public static void main(String[] args) {\n        System.setProperty(\"calendarfx.developer\", \"true\");\n        CalendarApp.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXApp/src/main/java/com/calendarfx/app/MonthViewApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.app;\n\nimport com.calendarfx.view.MonthView;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\npublic class MonthViewApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        MonthView monthView = new MonthView();\n\n        StackPane stackPane = new StackPane();\n        stackPane.getChildren().addAll(monthView); // introPane);\n\n        Scene scene = new Scene(stackPane);\n        primaryStage.setTitle(\"Month View\");\n        primaryStage.setScene(scene);\n        primaryStage.sizeToScene();\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXApp/src/main/java/module-info.java",
    "content": "module com.calendarfx.app {\n    requires transitive javafx.graphics;\n    requires fr.brouillard.oss.cssfx;\n    requires javafx.controls;\n    requires com.calendarfx.view;\n    requires atlantafx.base;\n    requires org.scenicview.scenicview;\n\n    exports com.calendarfx.app;\n}"
  },
  {
    "path": "CalendarFXGoogle/.gitignore",
    "content": "/target\n*.iml\n/bin/\n"
  },
  {
    "path": "CalendarFXGoogle/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n    <name>CalendarFXGoogle</name>\n    <artifactId>google</artifactId>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-controls</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.dlsc</groupId>\n            <artifactId>GMapsFX</artifactId>\n        </dependency>\n\n        <!-- Required for GMapsFX -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.api-client</groupId>\n            <artifactId>google-api-client</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.api-client</groupId>\n            <artifactId>google-api-client-java6</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.api-client</groupId>\n            <artifactId>google-api-client-jackson2</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.apis</groupId>\n            <artifactId>google-api-services-calendar</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.apis</groupId>\n            <artifactId>google-api-services-oauth2</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.code.geocoder-java</groupId>\n            <artifactId>geocoder-java</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.google.GoogleCalendarApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/GoogleCalendarApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google;\n\nimport com.calendarfx.google.view.GoogleCalendarAppView;\nimport com.calendarfx.view.CalendarView;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class GoogleCalendarApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        CalendarView calendarView = new CalendarView();\n        calendarView.setToday(LocalDate.now());\n        calendarView.setTime(LocalTime.now());\n        calendarView.setShowDeveloperConsole(Boolean.getBoolean(\"calendarfx.developer\"));\n\n        GoogleCalendarAppView appView = new GoogleCalendarAppView(calendarView);\n        appView.getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n\n        primaryStage.setTitle(\"Google Calendar\");\n        primaryStage.setScene(new Scene(appView));\n        primaryStage.setWidth(1400);\n        primaryStage.setHeight(950);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/BeanConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\n/**\n * Interface representing a converter from/to a single bean.\n *\n * Created by gdiaz on 20/02/2017.\n */\npublic interface BeanConverter<S, T> {\n\n    /**\n     * Converts the given source into an object of type defined by the target.\n     *\n     * @param source The object source to be converted.\n     * @return The target result of the convertion.\n     */\n    T convert(S source);\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/BidirectionalBeanConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\n/**\n * Converter in both directions.\n *\n * Created by gdiaz on 28/04/2017.\n */\npublic interface BidirectionalBeanConverter<L, R> extends BeanConverter<L, R> {\n\n    R leftToRight(L left);\n\n    L rightToLeft(R right);\n\n    @Override\n    default R convert(L source) {\n        return leftToRight(source);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/CalendarListEntryToGoogleCalendarConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.google.api.services.calendar.model.CalendarListEntry;\nimport com.google.api.services.calendar.model.EventReminder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Converts from google api to calendarfx api.\n *\n * Created by gdiaz on 20/02/2017.\n */\npublic final class CalendarListEntryToGoogleCalendarConverter implements BeanConverter<CalendarListEntry, GoogleCalendar> {\n    @Override\n    public GoogleCalendar convert(CalendarListEntry source) {\n        GoogleCalendar calendar = new GoogleCalendar();\n        calendar.setId(source.getId());\n        calendar.setName(source.getSummary());\n        calendar.setShortName(source.getSummary());\n        calendar.setPrimary(source.isPrimary());\n        calendar.setReadOnly(GoogleCalendar.isReadOnlyAccessRole(source.getAccessRole()));\n        List<GoogleEntryReminder> reminders = new ArrayList<>();\n        if (source.getDefaultReminders() != null) {\n            for (EventReminder r : source.getDefaultReminders()) {\n                reminders.add(new GoogleEntryReminder(r));\n            }\n        }\n        calendar.getDefaultReminders().setAll(reminders);\n        return calendar;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/EventToGoogleEntryConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.calendarfx.model.Interval;\nimport com.google.api.client.util.DateTime;\nimport com.google.api.services.calendar.model.Event;\nimport com.google.api.services.calendar.model.EventReminder;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.TimeZone;\n\n/**\n * BeanConverter between event (google api) and entry (calendarfx api).\n *\n * Created by gdiaz on 20/02/2017.\n */\npublic final class EventToGoogleEntryConverter implements BeanConverter<Event, GoogleEntry> {\n\n    @Override\n    public GoogleEntry convert(Event source) {\n        Objects.requireNonNull(source.getStart());\n        Objects.requireNonNull(source.getEnd());\n\n        GoogleEntry entry = new GoogleEntry();\n\n        /*\n         * TimeZone : Although Google allows different Start/End TimeZone, we\n         * always assume that start/end are in the TimeZone of the startDate.\n         */\n        ZoneId zoneId;\n        String stTimeZone = source.getStart().getTimeZone();\n\n        if (stTimeZone == null) {\n            zoneId = ZoneId.systemDefault();\n        } else {\n            zoneId = TimeZone.getTimeZone(stTimeZone).toZoneId();\n        }\n\n        // Start Time\n        DateTime stdt = source.getStart().getDate();\n        if (stdt == null) {\n            stdt = source.getStart().getDateTime();\n        }\n        ZoneOffset stOffset = ZoneOffset.ofTotalSeconds(stdt.getTimeZoneShift() * 60);\n        OffsetDateTime offsetSt = Instant.ofEpochMilli(stdt.getValue()).atOffset(stOffset);\n\n        // End Time\n        DateTime etdt = source.getEnd().getDate();\n        if (etdt == null) {\n            etdt = source.getEnd().getDateTime();\n        }\n        ZoneOffset etoffset = ZoneOffset.ofTotalSeconds(etdt.getTimeZoneShift() * 60);\n        OffsetDateTime offsetEt = Instant.ofEpochMilli(etdt.getValue()).atOffset(etoffset);\n\n        LocalDateTime startDateTime = offsetSt.toLocalDateTime();\n        if (stdt.isDateOnly() || offsetEt.toLocalTime().equals(LocalTime.MIDNIGHT)) {\n            offsetEt = offsetEt.minusDays(1);\n            entry.setInterval(new Interval(startDateTime.toLocalDate(), startDateTime.toLocalTime(), offsetEt.toLocalDate(), LocalTime.MAX));\n        } else {\n            entry.setInterval(new Interval(startDateTime, offsetEt.toLocalDateTime()));\n        }\n\n        if (source.getRecurrence() != null && !source.getRecurrence().isEmpty()) {\n            entry.setRecurrenceRule(source.getRecurrence().get(0));\n        }\n\n        if (source.getAttendees() != null) {\n            entry.getAttendees().setAll(source.getAttendees());\n        }\n\n        if (source.getReminders() != null) {\n            Event.Reminders reminders = source.getReminders();\n\n            if (Boolean.TRUE.equals(reminders.getUseDefault())) {\n                entry.setUseDefaultReminder(true);\n            } else if (reminders.getOverrides() != null) {\n                List<GoogleEntryReminder> entryReminders = new ArrayList<>();\n                for (EventReminder reminder : reminders.getOverrides()) {\n                    entryReminders.add(new GoogleEntryReminder(reminder));\n                }\n                entry.getReminders().setAll(entryReminders);\n            }\n        }\n\n        entry.setId(source.getId());\n        entry.setTitle(source.getSummary());\n        entry.setLocation(source.getLocation());\n        entry.setZoneId(zoneId);\n        entry.setFullDay(stdt.isDateOnly());\n        entry.setAttendeesCanModify(source.isGuestsCanModify());\n        entry.setAttendeesCanInviteOthers(source.isGuestsCanInviteOthers());\n        entry.setAttendeesCanSeeOthers(source.isGuestsCanSeeOtherGuests());\n        entry.setStatus(GoogleEntry.Status.fromName(source.getStatus()));\n        entry.setUserObject(source);\n\n        return entry;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/GoogleCalendarToCalendarConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.google.api.services.calendar.model.Calendar;\n\nimport java.time.ZoneId;\n\n/**\n * BeanConverter between calendarfx and google api.\n *\n * Created by gdiaz on 26/03/2017.\n */\npublic class GoogleCalendarToCalendarConverter implements BeanConverter<GoogleCalendar, Calendar> {\n    @Override\n    public Calendar convert(GoogleCalendar source) {\n        Calendar target = new Calendar();\n        target.setSummary(source.getName());\n        target.setTimeZone(ZoneId.systemDefault().getId());\n        return target;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/GoogleCalendarToCalendarListEntryConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.google.api.services.calendar.model.CalendarListEntry;\nimport com.google.api.services.calendar.model.EventReminder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Converts from calendar (calendarfx api) to calendar list entry (google api).\n *\n * Created by gdiaz on 20/02/2017.\n */\npublic final class GoogleCalendarToCalendarListEntryConverter implements BeanConverter<GoogleCalendar, CalendarListEntry> {\n    @Override\n    public CalendarListEntry convert(GoogleCalendar source) {\n        CalendarListEntry calendarListEntry = new CalendarListEntry();\n        calendarListEntry.setId(source.getId());\n        calendarListEntry.setSummary(source.getName());\n        calendarListEntry.setPrimary(source.isPrimary());\n        List<EventReminder> reminders = new ArrayList<>();\n        for (GoogleEntryReminder reminder : source.getDefaultReminders()) {\n            EventReminder er = new EventReminder();\n            er.setMethod(reminder.getMethod().getId());\n            er.setMinutes(reminder.getMinutes());\n            reminders.add(er);\n        }\n        calendarListEntry.setDefaultReminders(reminders);\n        return calendarListEntry;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/converter/GoogleEntryToEventConverter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.converter;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.google.api.client.util.DateTime;\nimport com.google.api.services.calendar.model.Event;\nimport com.google.api.services.calendar.model.EventDateTime;\nimport com.google.api.services.calendar.model.EventReminder;\n\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.TimeZone;\n\n/**\n * BeanConverter between entry (calendarfx api) and event (google api).\n *\n * Created by gdiaz on 20/02/2017.\n */\npublic final class GoogleEntryToEventConverter implements BeanConverter<GoogleEntry, Event> {\n    @Override\n    public Event convert(GoogleEntry source) {\n        EventDateTime startTime = new EventDateTime();\n        EventDateTime endTime = new EventDateTime();\n\n        ZonedDateTime st = source.getStartAsZonedDateTime();\n        ZonedDateTime et = source.getEndAsZonedDateTime();\n        TimeZone timeZone = TimeZone.getTimeZone(source.getZoneId());\n\n        if (source.getEndTime().equals(LocalTime.MAX)) {\n            et = et.plusDays(1).truncatedTo(ChronoUnit.DAYS);\n        }\n\n        if (source.isFullDay()) {\n            startTime.setDate(new DateTime(true, st.toInstant().toEpochMilli(), 0));\n            startTime.setTimeZone(timeZone.getID());\n            endTime.setDate(new DateTime(true, et.toInstant().toEpochMilli(), 0));\n            endTime.setTimeZone(timeZone.getID());\n        } else {\n            startTime.setDateTime(new DateTime(Date.from(st.toInstant())));\n            startTime.setTimeZone(timeZone.getID());\n            endTime.setDateTime(new DateTime(Date.from(et.toInstant())));\n            endTime.setTimeZone(timeZone.getID());\n        }\n\n        Event event = new Event();\n\n        if (source.getRecurrenceRule() != null) {\n            List<String> recurrence = new ArrayList<>();\n            recurrence.add(source.getRecurrenceRule());\n            event.setRecurrence(recurrence);\n        }\n\n        Event.Reminders reminders = new Event.Reminders();\n        reminders.setUseDefault(source.isUseDefaultReminder());\n        if (source.getReminders() != null) {\n            List<EventReminder> overrides = new ArrayList<>();\n            for (GoogleEntryReminder reminder : source.getReminders()) {\n                EventReminder override = new EventReminder();\n                override.setMethod(reminder.getMethod().getId());\n                override.setMinutes(reminder.getMinutes());\n                overrides.add(override);\n            }\n            reminders.setOverrides(overrides);\n        }\n\n        event.setId(source.existsInGoogle() ? source.getId() : null);\n        event.setSummary(source.getTitle());\n        event.setStart(startTime);\n        event.setEnd(endTime);\n        event.setAttendees(source.getAttendees());\n        event.setGuestsCanModify(source.isAttendeesCanModify());\n        event.setGuestsCanInviteOthers(source.isAttendeesCanInviteOthers());\n        event.setGuestsCanSeeOtherGuests(source.isAttendeesCanSeeOthers());\n        event.setLocation(source.getLocation());\n        event.setReminders(reminders);\n\n        return event;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/GoogleAccount.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport javafx.collections.ListChangeListener;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Class representing a Google account of a determinate user, which is owner of\n * the Calendars information.\n *\n * @author Gabriel Diaz, 03.03.2015.\n */\npublic class GoogleAccount extends CalendarSource {\n\n    private String id;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    /**\n     * Creates one single calendar with the given name and style.\n     *\n     * @param name  The name of the calendar.\n     * @param style The style of the calendar.\n     * @return The new google calendar.\n     */\n    public final GoogleCalendar createCalendar(String name, Calendar.Style style) {\n        GoogleCalendar calendar = new GoogleCalendar();\n        calendar.setName(name);\n        calendar.setStyle(style);\n        return calendar;\n    }\n\n    /**\n     * Gets the calendar marked as primary calendar for the google account.\n     *\n     * @return The primary calendar, {@code null} if not loaded.\n     */\n    public GoogleCalendar getPrimaryCalendar() {\n        return (GoogleCalendar) getCalendars().stream()\n                .filter(calendar -> ((GoogleCalendar) calendar).isPrimary())\n                .findFirst()\n                .orElse(null);\n    }\n\n    /**\n     * Gets all the google calendars hold by this source.\n     *\n     * @return The list of google calendars, always a new list.\n     */\n    public List<GoogleCalendar> getGoogleCalendars() {\n        List<GoogleCalendar> googleCalendars = new ArrayList<>();\n        for (Calendar calendar : getCalendars()) {\n            if (!(calendar instanceof GoogleCalendar)) {\n                continue;\n            }\n            googleCalendars.add((GoogleCalendar) calendar);\n        }\n        return googleCalendars;\n    }\n\n    /**\n     * Adds the given calendar listener in order to get notified when any calendar/entry changes.\n     *\n     * @param listeners The listener.\n     */\n    @SafeVarargs\n    public final void addCalendarListeners(ListChangeListener<Calendar>... listeners) {\n        if (listeners != null) {\n            for (ListChangeListener<Calendar> listener : listeners) {\n                getCalendars().addListener(listener);\n            }\n        }\n    }\n\n    /**\n     * Removes the listener from the ones being notified.\n     *\n     * @param listeners The listener\n     */\n    @SafeVarargs\n    public final void removeCalendarListeners(ListChangeListener<Calendar>... listeners) {\n        if (listeners != null) {\n            for (ListChangeListener<Calendar> listener : listeners) {\n                getCalendars().removeListener(listener);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/GoogleCalendar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.time.ZonedDateTime;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Calendar class that encapsulates the logic of a Google Calendar Entry.\n *\n * @author Gabriel Diaz, 10.02.2015.\n */\npublic class GoogleCalendar extends Calendar {\n\n    private static final String ACCESS_ROLE_READER = \"reader\";\n\n    private static final String ACCESS_ROLE_FREE_BUSY_READER = \"freeBusyReader\";\n\n    /**\n     * Static field used to enumerate the entries created in this calendar.\n     */\n    private static int entryConsecutive = 1;\n\n    /**\n     * Constructs a calendar using the given calendar service.\n     */\n    public GoogleCalendar() {\n        super();\n    }\n\n    /**\n     * Generates a new consecutive number each time is called. This always\n     * starts at 1.\n     *\n     * @return The consecutive generated.\n     */\n    private static int generateEntryConsecutive() {\n        return entryConsecutive++;\n    }\n\n    /**\n     * Represents the ID of the backend calendar entry, this field is generated\n     * only by Google.\n     */\n    private String id;\n\n    /**\n     * Gets the ID of the backend calendar entry, can be null if this calendar\n     * has not been saved in Google.\n     *\n     * @return The ID of the backend calendar entry.\n     */\n    public final String getId() {\n        return id;\n    }\n\n    /**\n     * Sets the ID for client side use; this is supposed to be called when this\n     * calendar is associated with its backend calendar entry.\n     *\n     * @param id\n     *            The new ID.\n     */\n    public final void setId(String id) {\n        this.id = id;\n    }\n\n    /**\n     * Indicating this calendar is the primary of the google account.\n     */\n    private boolean primary;\n\n    /**\n     * Gets the flag that indicates whether this calendar is primary or not. A\n     * primary calendar can be seen as the default calendar of the account.\n     *\n     * @return {@code true} when this calendar is primary, otherwise\n     *         {@code false}.\n     */\n    public final boolean isPrimary() {\n        return primary;\n    }\n\n    /**\n     * Sets the primary flag, supposed to be called when this calendar is\n     * associated with a backend calendar entry.\n     *\n     * @param primary\n     *            The value for the flag.\n     */\n    public final void setPrimary(boolean primary) {\n        this.primary = primary;\n    }\n\n    /**\n     * List storing the default reminders of the calendar, represents the\n     * default configuration for all the entries of this calendar and is\n     * supposed to be inherited.\n     */\n    private final ObservableList<GoogleEntryReminder> defaultReminders = FXCollections.observableArrayList();\n\n    /**\n     * Gets the list of default reminders configured for this calendar.\n     *\n     * @return The default remind configuration.\n     */\n    public final ObservableList<GoogleEntryReminder> getDefaultReminders() {\n        return defaultReminders;\n    }\n\n    /**\n     * Creates a new google entry by using the given parameters, this assigns a\n     * default name by using a consecutive number. The entry is of course\n     * associated to this calendar, but it is not sent to google for storing.\n     *\n     * @param start\n     *            The start date/time of the new entry.\n     * @param fullDay\n     *            A flag indicating if the new entry is going to be full day.\n     * @return The new google entry created, this is not send to server side.\n     * @see #generateEntryConsecutive()\n     */\n    public final GoogleEntry createEntry(ZonedDateTime start, boolean fullDay) {\n        GoogleEntry entry = new GoogleEntry();\n        entry.setTitle(\"New Entry \" + generateEntryConsecutive());\n        entry.setInterval(new Interval(start.toLocalDate(), start.toLocalTime(), start.toLocalDate(), start.toLocalTime().plusHours(1)));\n        entry.setFullDay(fullDay);\n        entry.setAttendeesCanInviteOthers(true);\n        entry.setAttendeesCanSeeOthers(true);\n        return entry;\n    }\n\n    /**\n     * Indicates whether the calendar exist in google calendar or not.\n     *\n     * @return a flag saying whether this calendar was already persisted or not.\n     */\n    public final boolean existsInGoogle() {\n        return id != null;\n    }\n\n    /**\n     * Checks whether the given access role means the calendar is read only.\n     *\n     * @param accessRole The access role to be analyzed.\n     * @return {@code true} if the access role matches {@link #ACCESS_ROLE_READER}\n     * or {@link #ACCESS_ROLE_FREE_BUSY_READER}.\n     */\n    public static boolean isReadOnlyAccessRole(String accessRole) {\n        return ACCESS_ROLE_READER.equals(accessRole) || ACCESS_ROLE_FREE_BUSY_READER.equals(accessRole);\n    }\n\n    /**\n     * Consumer to delegate the loading of entries to an external provider.\n     */\n    private IGoogleCalendarSearchTextProvider searchTextProvider;\n\n    public void setSearchTextProvider(IGoogleCalendarSearchTextProvider searchTextProvider) {\n        this.searchTextProvider = searchTextProvider;\n    }\n\n    @Override\n    public List<Entry<?>> findEntries(String text) {\n        if (searchTextProvider != null) {\n            searchTextProvider.search(this, text);\n        }\n        return super.findEntries(text);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        GoogleCalendar that = (GoogleCalendar) o;\n        return Objects.equals(id, that.id);\n    }\n\n    @Override\n    public int hashCode() {\n        return id != null ? id.hashCode() : 0;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/GoogleCalendarEvent.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\nimport com.calendarfx.model.CalendarEvent;\nimport javafx.event.EventType;\n\n/**\n * Extension of the {@link CalendarEvent} which allows to know externally about\n * changes in {@link GoogleCalendar calendars}.\n *\n * @author Gabriel Diaz, 07.03.2015.\n */\npublic class GoogleCalendarEvent extends CalendarEvent {\n\n    private static final long serialVersionUID = 8064360122175452019L;\n\n    /**\n     * Event type saying that attendees of the google entry have been changed.\n     */\n    public static final EventType<GoogleCalendarEvent> ENTRY_ATTENDEES_CHANGED = new EventType<>(\n            ENTRY_CHANGED, \"ENTRY_ATTENDEES_CHANGED\");\n\n    /**\n     * Event type saying whether attendees can see others or not.\n     */\n    public static final EventType<GoogleCalendarEvent> ENTRY_ATTENDEES_CAN_SEE_OTHERS_CHANGED = new EventType<>(\n            ENTRY_CHANGED, \"ENTRY_ATTENDEES_CAN_SEE_OTHERS_CHANGED\");\n\n    /**\n     * Event type saying whether attendees can invite others or not.\n     */\n    public static final EventType<GoogleCalendarEvent> ENTRY_ATTENDEES_CAN_INVITE_CHANGED = new EventType<>(\n            ENTRY_CHANGED, \"ENTRY_ATTENDEES_CAN_INVITE_CHANGED\");\n\n    /**\n     * Event type saying whether attendees can edit the google entry others or\n     * not.\n     */\n    public static final EventType<GoogleCalendarEvent> ENTRY_ATTENDEES_CAN_MODIFY_CHANGED = new EventType<>(\n            ENTRY_CHANGED, \"ENTRY_ATTENDEES_CAN_MODIFY_CHANGED\");\n\n    /**\n     * Event type saying that reminders of the google entry have been changed.\n     */\n    public static final EventType<GoogleCalendarEvent> ENTRY_REMINDERS_CHANGED = new EventType<>(\n            ENTRY_CHANGED, \"ENTRY_REMINDERS_CHANGED\");\n\n    /**\n     * Creates a google calendar event to notify some changes in the given\n     * google entry.\n     *\n     * @param eventType\n     *            The type of event.\n     * @param calendar\n     *            The calendar which the entry belongs to.\n     * @param entry\n     *            The entry affected.\n     */\n    GoogleCalendarEvent(EventType<GoogleCalendarEvent> eventType, GoogleCalendar calendar, GoogleEntry entry) {\n        super(eventType, calendar, entry);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/GoogleEntry.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\nimport com.calendarfx.model.Entry;\nimport com.google.api.services.calendar.model.Event;\nimport com.google.api.services.calendar.model.EventAttendee;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\n\nimport java.util.Objects;\n\n/**\n * Custom entry representing a google event. This contains all the required\n * information for a single event of google calendar.\n *\n * @author Gabriel Diaz, 07.03.2015.\n */\npublic class GoogleEntry extends Entry<Event> {\n\n    public GoogleEntry() {\n        super();\n        reminders.addListener((ListChangeListener<GoogleEntryReminder>) c -> {\n            while (c.next()) {\n                if (c.wasAdded()) {\n                    for (GoogleEntryReminder r : c.getAddedSubList()) {\n                        r.addListener(remindersListener);\n                    }\n                }\n\n                if (c.wasRemoved()) {\n                    for (GoogleEntryReminder r : c.getRemoved()) {\n                        r.removeListener(remindersListener);\n                    }\n                }\n            }\n        });\n        reminders.addListener(remindersListener);\n        attendees.addListener((Observable obs) -> {\n            if (getCalendar() instanceof GoogleCalendar) {\n                getCalendar().fireEvent(new GoogleCalendarEvent(\n                        GoogleCalendarEvent.ENTRY_ATTENDEES_CHANGED, (GoogleCalendar) getCalendar(), GoogleEntry.this));\n            }\n        });\n    }\n\n    private final ObjectProperty<Status> status = new SimpleObjectProperty<>(this, \"status\");\n\n    public final ObjectProperty<Status> statusProperty() {\n        return status;\n    }\n\n    public final Status getStatus() {\n        return statusProperty().get();\n    }\n\n    public final void setStatus(Status status) {\n        statusProperty().set(status);\n    }\n\n    private final BooleanProperty attendeesCanModify = new SimpleBooleanProperty(this, \"attendeesCanModify\") {\n        @Override\n        public void set(boolean newValue) {\n            boolean oldValue = get();\n\n            if (!Objects.equals(oldValue, newValue)) {\n                super.set(newValue);\n\n                if (getCalendar() instanceof GoogleCalendar) {\n                    getCalendar().fireEvent(new GoogleCalendarEvent(\n                            GoogleCalendarEvent.ENTRY_ATTENDEES_CAN_MODIFY_CHANGED,\n                            (GoogleCalendar) getCalendar(), GoogleEntry.this));\n                }\n\n                if (newValue) {\n                    setAttendeesCanInviteOthers(true);\n                    setAttendeesCanSeeOthers(true);\n                }\n            }\n        }\n    };\n\n    public final BooleanProperty attendeesCanModifyProperty() {\n        return attendeesCanModify;\n    }\n\n    public final boolean isAttendeesCanModify() {\n        return attendeesCanModifyProperty().get();\n    }\n\n    public final void setAttendeesCanModify(boolean attendeesCanModify) {\n        attendeesCanModifyProperty().set(attendeesCanModify);\n    }\n\n    private final BooleanProperty attendeesCanInviteOthers = new SimpleBooleanProperty(\n            this, \"attendeesCanInviteOthers\", true) {\n\n        @Override\n        public void set(boolean newValue) {\n            boolean oldValue = get();\n\n            if (!Objects.equals(oldValue, newValue)) {\n                super.set(newValue);\n\n                if (getCalendar() instanceof GoogleCalendar) {\n                    getCalendar().fireEvent(new GoogleCalendarEvent(\n                            GoogleCalendarEvent.ENTRY_ATTENDEES_CAN_INVITE_CHANGED,\n                            (GoogleCalendar) getCalendar(), GoogleEntry.this));\n                }\n            }\n        }\n    };\n\n    public final BooleanProperty attendeesCanInviteOthersProperty() {\n        return attendeesCanInviteOthers;\n    }\n\n    public final boolean isAttendeesCanInviteOthers() {\n        return attendeesCanInviteOthersProperty().get();\n    }\n\n    public final void setAttendeesCanInviteOthers(\n            boolean attendeesCanInviteOthers) {\n        attendeesCanInviteOthersProperty().set(attendeesCanInviteOthers);\n    }\n\n    private final BooleanProperty attendeesCanSeeOthers = new SimpleBooleanProperty(\n            this, \"attendeesCanSeeOthers\", true) {\n\n        @Override\n        public void set(boolean newValue) {\n            boolean oldValue = get();\n\n            if (!Objects.equals(oldValue, newValue)) {\n                super.set(newValue);\n\n                if (getCalendar() instanceof GoogleCalendar) {\n                    getCalendar().fireEvent(new GoogleCalendarEvent(\n                            GoogleCalendarEvent.ENTRY_ATTENDEES_CAN_SEE_OTHERS_CHANGED,\n                            (GoogleCalendar) getCalendar(), GoogleEntry.this));\n                }\n            }\n        }\n    };\n\n    public final BooleanProperty attendeesCanSeeOthersProperty() {\n        return attendeesCanSeeOthers;\n    }\n\n    public final boolean isAttendeesCanSeeOthers() {\n        return attendeesCanSeeOthersProperty().get();\n    }\n\n    public final void setAttendeesCanSeeOthers(boolean attendeesCanSeeOthers) {\n        attendeesCanSeeOthersProperty().set(attendeesCanSeeOthers);\n    }\n\n    private final BooleanProperty useDefaultReminder = new SimpleBooleanProperty(this, \"useDefaultReminder\");\n\n    public final BooleanProperty useDefaultReminderProperty() {\n        return useDefaultReminder;\n    }\n\n    public final boolean isUseDefaultReminder() {\n        return useDefaultReminderProperty().get();\n    }\n\n    public final void setUseDefaultReminder(boolean useDefaultReminder) {\n        useDefaultReminderProperty().set(useDefaultReminder);\n    }\n\n    private final ObservableList<EventAttendee> attendees = FXCollections.observableArrayList();\n\n    public ObservableList<EventAttendee> getAttendees() {\n        return attendees;\n    }\n\n    private final InvalidationListener remindersListener = obs -> {\n        if (getCalendar() instanceof GoogleCalendar) {\n            GoogleCalendar calendar = (GoogleCalendar) getCalendar();\n            calendar.fireEvent(new GoogleCalendarEvent(GoogleCalendarEvent.ENTRY_REMINDERS_CHANGED, calendar, GoogleEntry.this));\n        }\n    };\n\n    private final ObservableList<GoogleEntryReminder> reminders = FXCollections.observableArrayList();\n\n    public ObservableList<GoogleEntryReminder> getReminders() {\n        return reminders;\n    }\n\n    @Override\n    public GoogleEntry createRecurrence() {\n        GoogleEntry entry = new GoogleEntry();\n        entry.setStatus(getStatus());\n        entry.setAttendeesCanModify(isAttendeesCanModify());\n        entry.setAttendeesCanInviteOthers(isAttendeesCanInviteOthers());\n        entry.setAttendeesCanSeeOthers(isAttendeesCanSeeOthers());\n        entry.getAttendees().setAll(getAttendees());\n        entry.getReminders().setAll(getReminders());\n        return entry;\n    }\n\n    /**\n     * Indicates whether the entry exist in google calendar or not.\n     *\n     * @return a flag saying whether this entry was already persisted or not.\n     */\n    public final boolean existsInGoogle() {\n        return getUserObject() != null;\n    }\n\n    @Override\n    public String toString() {\n        return \"Google Entry: \" + getTitle();\n    }\n\n    /**\n     * Enumeration representing the status of the google entry.\n     *\n     * @author Gabriel Diaz, 22.03.2015.\n     */\n    public enum Status {\n\n        CONFIRMED(\"confirmed\"),\n\n        TENTATIVE(\"tentative\"),\n\n        CANCELLED(\"cancelled\");\n\n        private final String name;\n\n        Status(String name) {\n            this.name = name;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public static Status fromName(String name) {\n            for (Status status : values()) {\n                if (status.getName().equals(name)) {\n                    return status;\n                }\n            }\n\n            return null;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/GoogleEntryReminder.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\nimport com.google.api.services.calendar.model.EventReminder;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\n/**\n * Remind method.\n *\n * Created by gdiaz on 28/04/2017.\n */\npublic class GoogleEntryReminder {\n\n    public GoogleEntryReminder() {\n        super();\n    }\n\n    public GoogleEntryReminder(EventReminder reminder) {\n        super();\n        setMethod(RemindMethod.fromId(reminder.getMethod()));\n        setMinutes(reminder.getMinutes());\n    }\n\n    public void addListener(InvalidationListener listener) {\n        methodProperty().addListener(listener);\n        minutesProperty().addListener(listener);\n    }\n\n    public void removeListener(InvalidationListener listener) {\n        methodProperty().removeListener(listener);\n        minutesProperty().addListener(listener);\n    }\n\n    private final ObjectProperty<RemindMethod> method = new SimpleObjectProperty<>(this, \"method\");\n\n    public final ObjectProperty<RemindMethod> methodProperty() {\n        return method;\n    }\n\n    public final RemindMethod getMethod() {\n        return methodProperty().get();\n    }\n\n    public final void setMethod(RemindMethod method) {\n        methodProperty().set(method);\n    }\n\n    private final ObjectProperty<Integer> minutes = new SimpleObjectProperty<>(this, \"minutes\");\n\n    public final ObjectProperty<Integer> minutesProperty() {\n        return minutes;\n    }\n\n    public final Integer getMinutes() {\n        return minutesProperty().get();\n    }\n\n    public final void setMinutes(Integer minutes) {\n        minutesProperty().set(minutes);\n    }\n\n    /**\n     * Enumeration representing the available notification types.\n     *\n     * @author Gabriel Diaz, 07.03.2015.\n     */\n    public enum RemindMethod {\n\n        POPUP(\"popup\", \"Popup\"), EMAIL(\"email\", \"Email\");\n\n        private final String id;\n        private final String name;\n\n        RemindMethod(String id, String name) {\n            this.id = id;\n            this.name = name;\n        }\n\n        public String getId() {\n            return id;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        @Override\n        public String toString() {\n            return getName();\n        }\n\n        /**\n         * Gets a remind method.\n         *\n         * @param id The id of the remind method.\n         * @return The reminder method constant that matches the id.\n         */\n        public static RemindMethod fromId(String id) {\n            if (id != null) {\n                for (RemindMethod method : values()) {\n                    if (method.id.equals(id)) {\n                        return method;\n                    }\n                }\n            }\n            return POPUP;\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/model/IGoogleCalendarSearchTextProvider.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.model;\n\n/**\n * Objects that provides external searching of entries by a given text on a single calendar.\n *\n * Created by gdiaz on 5/05/2017.\n */\npublic interface IGoogleCalendarSearchTextProvider {\n\n    void search(GoogleCalendar calendar, String searchText);\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/service/BeanConverterService.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.service;\n\nimport com.calendarfx.google.converter.BeanConverter;\nimport com.calendarfx.google.converter.CalendarListEntryToGoogleCalendarConverter;\nimport com.calendarfx.google.converter.EventToGoogleEntryConverter;\nimport com.calendarfx.google.converter.GoogleCalendarToCalendarConverter;\nimport com.calendarfx.google.converter.GoogleCalendarToCalendarListEntryConverter;\nimport com.calendarfx.google.converter.GoogleEntryToEventConverter;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.google.api.services.calendar.model.Calendar;\nimport com.google.api.services.calendar.model.CalendarListEntry;\nimport com.google.api.services.calendar.model.Event;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * Service that provides convertion from one object to another based on the class.\n *\n * Created by gdiaz on 22/02/2017.\n */\npublic class BeanConverterService {\n\n    private static BeanConverterService instance;\n\n    public static BeanConverterService getInstance() {\n        if (instance == null) {\n            instance = new BeanConverterService();\n        }\n        return instance;\n    }\n\n    private final Map<Pair<Class<?>, Class<?>>, BeanConverter<?, ?>> converters = new HashMap<>();\n\n    private BeanConverterService() {\n        converters.put(Pair.of(GoogleEntry.class, Event.class), new GoogleEntryToEventConverter());\n        converters.put(Pair.of(Event.class, GoogleEntry.class), new EventToGoogleEntryConverter());\n        converters.put(Pair.of(GoogleCalendar.class, CalendarListEntry.class), new GoogleCalendarToCalendarListEntryConverter());\n        converters.put(Pair.of(CalendarListEntry.class, GoogleCalendar.class), new CalendarListEntryToGoogleCalendarConverter());\n        converters.put(Pair.of(GoogleCalendar.class, Calendar.class), new GoogleCalendarToCalendarConverter());\n    }\n\n    <S, T> T convert(S source, Class<T> targetClass) {\n        if (canConvert(source.getClass(), targetClass)) {\n            BeanConverter<S, T> converter = getConverter((Class<S>) source.getClass(), targetClass);\n            return converter.convert(source);\n        }\n        throw new UnsupportedOperationException(\"The object \" + source + \" cannot be converted to \" + targetClass);\n    }\n\n    private <S, T> boolean canConvert(Class<S> sourceClass, Class<T> targetClass) {\n        return getConverter(sourceClass, targetClass) != null;\n    }\n\n    private <S, T> BeanConverter<S, T> getConverter(Class<S> sourceClass, Class<T> targetClass) {\n        return (BeanConverter<S, T>) converters.get(Pair.of(sourceClass, targetClass));\n    }\n\n    private static class Pair<S, T> {\n\n        private S source;\n        private T target;\n\n        Pair(S source, T target) {\n            this.source = source;\n            this.target = target;\n        }\n\n        public S getSource() {\n            return source;\n        }\n\n        public void setSource(S source) {\n            this.source = source;\n        }\n\n        public T getTarget() {\n            return target;\n        }\n\n        public void setTarget(T target) {\n            this.target = target;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n\n            Pair<?, ?> pair = (Pair<?, ?>) o;\n\n            if (!Objects.equals(source, pair.source)) return false;\n            return Objects.equals(target, pair.target);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = source != null ? source.hashCode() : 0;\n            result = 31 * result + (target != null ? target.hashCode() : 0);\n            return result;\n        }\n\n        public static <S, T> Pair<S, T> of(S source, T target) {\n            return new Pair<>(source, target);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/service/GoogleCalendarService.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.service;\n\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.google.api.client.util.DateTime;\nimport com.google.api.services.calendar.Calendar;\nimport com.google.api.services.calendar.model.CalendarListEntry;\nimport com.google.api.services.calendar.model.Event;\n\nimport java.io.IOException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Encapsulation of the google calendar service that provides some methods for\n * CRUD operations on calendars and events.\n *\n * @author Gabriel Diaz, 14.03.2015.\n */\npublic class GoogleCalendarService {\n\n    private final Calendar dao;\n    private final BeanConverterService converter;\n\n    GoogleCalendarService(Calendar dao) {\n        this.dao = Objects.requireNonNull(dao);\n        this.converter = BeanConverterService.getInstance();\n    }\n\n    /**\n     * Inserts a calendar into the google calendar.\n     *\n     * @param calendar The calendar to be inserted.\n     * @throws IOException For unexpected errors.\n     */\n    public void insertCalendar(GoogleCalendar calendar) throws IOException {\n        com.google.api.services.calendar.model.Calendar cal;\n        cal = converter.convert(calendar, com.google.api.services.calendar.model.Calendar.class);\n        cal = dao.calendars().insert(cal).execute();\n        calendar.setId(cal.getId());\n    }\n\n    /**\n     * Saves the updates done on the calendar into google calendar api.\n     *\n     * @param calendar The calendar to be updated.\n     * @throws IOException For unexpected errors.\n     */\n    public void updateCalendar(GoogleCalendar calendar) throws IOException {\n        CalendarListEntry calendarListEntry = converter.convert(calendar, CalendarListEntry.class);\n        dao.calendarList().update(calendarListEntry.getId(), calendarListEntry).execute();\n    }\n\n    /**\n     * Performs an immediate delete request on the google calendar api.\n     *\n     * @param calendar The calendar to be removed.\n     * @throws IOException For unexpected errors\n     */\n    public void deleteCalendar(GoogleCalendar calendar) throws IOException {\n        dao.calendars().delete(calendar.getId()).execute();\n    }\n\n    /**\n     * Performs an immediate insert operation on google server by sending the\n     * information provided by the given google entry. The entry is associated\n     * to this calendar.\n     *\n     * @param entry The entry to be inserted in a backend google calendar.\n     * @param calendar The calendar in which the entry will be inserted.\n     * @return The same instance received.\n     * @throws IOException For unexpected errors\n     */\n    public GoogleEntry insertEntry(GoogleEntry entry, GoogleCalendar calendar) throws IOException {\n        Event event = converter.convert(entry, Event.class);\n        event = dao.events().insert(calendar.getId(), event).execute();\n        entry.setId(event.getId());\n        entry.setUserObject(event);\n        return entry;\n    }\n\n    /**\n     * Performs an immediate update operation on google server by sending the\n     * information stored by the given google entry.\n     *\n     * @param entry The entry to be updated in a backend google calendar.\n     * @return The same instance received.\n     * @throws IOException For unexpected errors\n     */\n    public GoogleEntry updateEntry(GoogleEntry entry) throws IOException {\n        GoogleCalendar calendar = (GoogleCalendar) entry.getCalendar();\n        Event event = converter.convert(entry, Event.class);\n        dao.events().update(calendar.getId(), event.getId(), event).execute();\n        return entry;\n    }\n\n    /**\n     * Sends a delete request to the google server for the given entry.\n     *\n     * @param entry The entry to be deleted from the backend google calendar.\n     * @param calendar The calendar from the entry was deleted.\n     * @throws IOException For unexpected errors.\n     */\n    public void deleteEntry(GoogleEntry entry, GoogleCalendar calendar) throws IOException {\n        dao.events().delete(calendar.getId(), entry.getId()).execute();\n    }\n\n    /**\n     * Moves an entry from one calendar to another.\n     *\n     * @param entry The entry to be moved.\n     * @param from The current calendar.\n     * @param to The future calendar.\n     * @return The entry updated.\n     * @throws IOException For unexpected errors.\n     */\n    public GoogleEntry moveEntry(GoogleEntry entry, GoogleCalendar from, GoogleCalendar to) throws IOException {\n        dao.events().move(from.getId(), entry.getId(), to.getId()).execute();\n        return entry;\n    }\n\n    /**\n     * Gets the list of all calendars available in the account.\n     *\n     * @return A non-null list of all calendars.\n     * @throws IOException For unexpected errors.\n     */\n    public List<GoogleCalendar> getCalendars() throws IOException {\n        List<CalendarListEntry> calendarListEntries = dao.calendarList().list().execute().getItems();\n        List<GoogleCalendar> calendars = new ArrayList<>();\n\n        if (calendarListEntries != null && !calendarListEntries.isEmpty()) {\n            for (int i = 0; i < calendarListEntries.size(); i++) {\n                CalendarListEntry calendarListEntry = calendarListEntries.get(i);\n                GoogleCalendar calendar = converter.convert(calendarListEntry, GoogleCalendar.class);\n                calendar.setStyle(com.calendarfx.model.Calendar.Style.getStyle(i));\n                calendars.add(calendar);\n            }\n        }\n\n        return calendars;\n    }\n\n    /**\n     * Gets a list of entries belonging to the given calendar defined between the given range of time. Recurring events\n     * are not expanded, always recurrence is handled manually within the framework.\n     *\n     * @param calendar The calendar owner of the entries.\n     * @param startDate The start date, not nullable.\n     * @param endDate The end date, not nullable\n     * @param zoneId The timezone in which the dates are represented.\n     * @return A non-null list of entries.\n     * @throws IOException For unexpected errors\n     */\n    public List<GoogleEntry> getEntries(GoogleCalendar calendar, LocalDate startDate, LocalDate endDate, ZoneId zoneId) throws IOException {\n        if (!calendar.existsInGoogle()) {\n            return new ArrayList<>(0);\n        }\n\n        ZonedDateTime st = ZonedDateTime.of(startDate, LocalTime.MIN, zoneId);\n        ZonedDateTime et = ZonedDateTime.of(endDate, LocalTime.MAX, zoneId);\n        String calendarId = URLDecoder.decode(calendar.getId(), StandardCharsets.UTF_8);\n\n        List<Event> events = dao.events()\n                .list(calendarId)\n                .setTimeMin(new DateTime(Date.from(st.toInstant())))\n                .setTimeMax(new DateTime(Date.from(et.toInstant())))\n                .setSingleEvents(false)\n                .setShowDeleted(false)\n                .execute()\n                .getItems();\n\n        return toGoogleEntries(events);\n    }\n\n    /**\n     * Gets a list of entries that matches the given text. Recurring events\n     * are not expanded, always recurrence is handled manually within the framework.\n     *\n     * @param calendar The calendar owner of the entries.\n     * @param searchText The search text\n     * @return A non-null list of entries.\n     * @throws IOException For unexpected errors\n     */\n    public List<GoogleEntry> getEntries(GoogleCalendar calendar, String searchText) throws IOException {\n        if (!calendar.existsInGoogle()) {\n            return new ArrayList<>(0);\n        }\n\n        String calendarId = URLDecoder.decode(calendar.getId(), StandardCharsets.UTF_8);\n\n        List<Event> events = dao.events()\n                .list(calendarId)\n                .setQ(searchText)\n                .setSingleEvents(false)\n                .setShowDeleted(false)\n                .execute()\n                .getItems();\n\n        return toGoogleEntries(events);\n    }\n\n    private List<GoogleEntry> toGoogleEntries(List<Event> events) {\n        List<GoogleEntry> entries = new ArrayList<>();\n        if (events != null && !events.isEmpty()) {\n            for (Event event : events) {\n                entries.add(converter.convert(event, GoogleEntry.class));\n            }\n        }\n        return entries;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/service/GoogleConnector.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.service;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.google.api.client.auth.oauth2.AuthorizationCodeFlow;\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.auth.oauth2.StoredCredential;\nimport com.google.api.client.auth.oauth2.TokenResponse;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleOAuthConstants;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.jackson2.JacksonFactory;\nimport com.google.api.client.util.store.DataStore;\nimport com.google.api.client.util.store.DataStoreFactory;\nimport com.google.api.client.util.store.FileDataStoreFactory;\nimport com.google.api.services.calendar.Calendar;\nimport com.google.api.services.calendar.CalendarScopes;\nimport com.google.api.services.oauth2.Oauth2;\nimport com.google.api.services.oauth2.Oauth2Scopes;\nimport com.google.api.services.oauth2.model.Userinfoplus;\nimport com.google.code.geocoder.Geocoder;\nimport com.google.common.collect.Lists;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.security.GeneralSecurityException;\nimport java.util.List;\n\n/**\n * Utility class used to connect to google services.\n *\n * @author Gabriel Diaz, 16.12.2014\n */\npublic final class GoogleConnector {\n\n    /** Application name for identification purposes. */\n    private static final String APPLICATION_NAME = \"flexcalendarfxapp\";\n\n    /** Directory used to store the user credentials. */\n    private static final File CREDENTIALS_DIRECTORY = new File(System.getProperty(\"user.home\"), \".store/flexcalendarfx\");\n\n    /** Default scopes used by the Google calendar application. */\n    private static final List<String> SCOPES = Lists.newArrayList(CalendarScopes.CALENDAR, Oauth2Scopes.USERINFO_PROFILE);\n\n    /** The instance of this singleton class. */\n    private static GoogleConnector instance;\n\n    /** JSON implementation. */\n    private final JsonFactory jsonFactory;\n\n    /** HTTP transport used for transferring data. */\n    private final HttpTransport httpTransport;\n\n    /** Secrets of the FX calendar application. */\n    private final GoogleClientSecrets secrets;\n\n    /**  Factory to create the data store object. */\n    private final DataStoreFactory dataStoreFactory;\n\n    /** Flow to get the access token. */\n    private AuthorizationCodeFlow flow;\n\n    /** Service that allows to manipulate Google Calendars. */\n    private GoogleCalendarService calendarService;\n\n    /** Service that allows to use Google Geolocalization. */\n    private GoogleGeocoderService geoService;\n\n    /**\n     * Instances the google connector configuring all required resources.\n     */\n    private GoogleConnector() throws IOException, GeneralSecurityException {\n        super();\n        // 1. JSON library\n        jsonFactory = JacksonFactory.getDefaultInstance();\n\n        // 2. Configure HTTP transport\n        httpTransport = GoogleNetHttpTransport.newTrustedTransport();\n\n        // 3. Load the credentials\n        secrets = GoogleClientSecrets.load(jsonFactory, new InputStreamReader(GoogleConnector.class.getResourceAsStream(\"client-secrets.json\")));\n\n        // 4. Configure the authentication flow\n        dataStoreFactory = new FileDataStoreFactory(CREDENTIALS_DIRECTORY);\n\n        // 5. Create flow\n        imp_buildAuthorizationFlow();\n    }\n\n    /**\n     * On demand instance creator method used to get the single instance of this\n     * google authenticator class.\n     *\n     * @return The single instance of this class, if the instance does not exist,\n     *         this is immediately created.\n     */\n    public static GoogleConnector getInstance() {\n        if (instance == null) {\n            try {\n                instance = new GoogleConnector();\n            } catch (Exception e) {\n                throw new RuntimeException(\"The GoogleConnector could not be instanced!\", e);\n            }\n        }\n        return instance;\n    }\n\n    /**\n     * Starts the authorization process but using the provided authorization\n     * code, so this will not attempt to get it from the user.\n     *\n     * @param accountId The account to be authorized.\n     * @param authorizationCode The code used as key of authorization.\n     * @throws IOException If storing the code fails.\n     */\n    synchronized void authorize(String accountId, String authorizationCode) throws IOException {\n        impl_storeCredential(accountId, authorizationCode);\n    }\n\n    /**\n     * Deletes the stored credentials for the given account id. This means the\n     * next time the user must authorize the app to access his calendars.\n     *\n     * @param accountId\n     *            The identifier of the account.\n     */\n    synchronized void removeCredential(String accountId) throws IOException {\n        DataStore<StoredCredential> sc = StoredCredential.getDefaultDataStore(dataStoreFactory);\n        sc.delete(accountId);\n        calendarService = null;\n        geoService = null;\n    }\n\n    /**\n     * Checks if the given account id has already been authorized and the\n     * user granted access to his calendars info.\n     *\n     * @param accountId\n     *            The identifier of the account used internally by the application.\n     * @return {@code true} if the account has already been set up, otherwise\n     *         {@code false}.\n     */\n    boolean isAuthorized(String accountId) {\n        try {\n            DataStore<StoredCredential> sc = StoredCredential.getDefaultDataStore(dataStoreFactory);\n            return sc.containsKey(accountId);\n        } catch (IOException e) {\n            return false;\n        }\n    }\n\n    /**\n     * Generates a valid URL to let the user authorize access his Google\n     * calendar information.\n     *\n     * @return The valid web URL.\n     */\n    public String getAuthorizationURL() {\n        return flow.newAuthorizationUrl().setRedirectUri(GoogleOAuthConstants.OOB_REDIRECT_URI).build();\n    }\n\n    /**\n     * Gets the service to access geo localization methods; using this service does not require authentication.\n     *\n     * @return The service created, this is on demand created.\n     */\n    public synchronized GoogleGeocoderService getGeocoderService() {\n        if (geoService == null) {\n            geoService = new GoogleGeocoderService(new Geocoder());\n        }\n        return geoService;\n    }\n\n    /**\n     * Instances a new calendar service for the given google account user name.\n     * This requires previous authorization to get the service, so if the user\n     * has not granted access to his data, this method will start the\n     * authorization process automatically; this attempts to open the login google page in the\n     * default browser.\n     *\n     * @param accountId\n     *            The google account.\n     * @return The calendar service, this can be null if the account cannot be\n     *         authenticated.\n     * @throws IOException If the account has not been authenticated.\n     */\n    public synchronized GoogleCalendarService getCalendarService(String accountId) throws IOException {\n        if (calendarService == null) {\n            Credential credential = impl_getStoredCredential(accountId);\n            if (credential == null) {\n                throw new UnsupportedOperationException(\"The account has not been authorized yet!\");\n            }\n            calendarService = new GoogleCalendarService(impl_createService(credential));\n        }\n        return calendarService;\n    }\n\n    /**\n     * Requests the user info for the given account. This requires previous\n     * authorization from the user, so this might start the process.\n     *\n     * @param accountId\n     *            The id of the account to get the user info.\n     * @return The user info bean.\n     * @throws IOException If the account cannot be accessed.\n     */\n    GoogleAccount getAccountInfo(String accountId) throws IOException {\n        Credential credential = impl_getStoredCredential(accountId);\n        if (credential == null) {\n            throw new UnsupportedOperationException(\"The account has not been authorized yet!\");\n        }\n        Userinfoplus info = impl_requestUserInfo(credential);\n        GoogleAccount account = new GoogleAccount();\n        account.setId(accountId);\n        account.setName(info.getName());\n        return account;\n    }\n\n    // ::::::::::::::::::    PRIVATE STUFF    :::::::::::::::::::::\n\n    private Calendar impl_createService(Credential credentials) {\n        return new Calendar.Builder(httpTransport, jsonFactory, credentials).setApplicationName(APPLICATION_NAME).build();\n    }\n\n    private Credential impl_getStoredCredential(String accountId) throws IOException {\n        return flow.loadCredential(accountId);\n    }\n\n    private void impl_storeCredential(String accountId, String authorizationCode) throws IOException {\n        TokenResponse response = flow.newTokenRequest(authorizationCode).setRedirectUri(GoogleOAuthConstants.OOB_REDIRECT_URI).execute();\n        flow.createAndStoreCredential(response, accountId);\n    }\n\n    private Userinfoplus impl_requestUserInfo(Credential credentials) throws IOException {\n        Oauth2 userInfoService = new Oauth2.Builder(httpTransport, jsonFactory, credentials).setApplicationName(APPLICATION_NAME).build();\n        return userInfoService.userinfo().get().execute();\n    }\n\n    private void imp_buildAuthorizationFlow() throws IOException {\n        flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, secrets, SCOPES).setDataStoreFactory(dataStoreFactory).build();\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/service/GoogleGeocoderService.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.service;\n\nimport com.google.code.geocoder.Geocoder;\nimport com.google.code.geocoder.model.GeocodeResponse;\nimport com.google.code.geocoder.model.GeocoderGeometry;\nimport com.google.code.geocoder.model.GeocoderRequest;\nimport com.google.code.geocoder.model.GeocoderResult;\nimport com.google.code.geocoder.model.GeocoderStatus;\n\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * BeanConverter class that allows to transform from a location (String) to a\n * coordinate and vice versa. Uses the Google GeoService, which is responsible\n * of the transformation.\n *\n * @author Gabriel Diaz, 17.02.2015.\n */\npublic final class GoogleGeocoderService {\n\n    private final Geocoder geocoder;\n\n    GoogleGeocoderService(Geocoder geocoder) {\n        this.geocoder = geocoder;\n    }\n\n    public GeocoderGeometry locationToCoordinate(String location) throws IOException {\n        GeocoderGeometry coordinate = null;\n\n        if (location != null && !location.isEmpty()) {\n            GeocoderRequest request = new GeocoderRequest();\n            request.setAddress(location);\n\n            GeocodeResponse response = geocoder.geocode(request);\n            if (response.getStatus() == GeocoderStatus.OK) {\n                List<GeocoderResult> results = response.getResults();\n\n                for (GeocoderResult result : results) {\n                    GeocoderGeometry geometry = result.getGeometry();\n                    coordinate = geometry;\n                    break;\n                }\n            }\n        }\n\n        return coordinate;\n    }\n\n    public String coordinateToLocation(GeocoderGeometry coordinate) throws IOException {\n        String location = null;\n\n        if (coordinate != null) {\n            GeocoderRequest request = new GeocoderRequest();\n            request.setLocation(coordinate.getLocation());\n            request.setBounds(coordinate.getBounds());\n\n            GeocodeResponse response = geocoder.geocode(request);\n            if (response.getStatus() == GeocoderStatus.OK) {\n                List<GeocoderResult> results = response.getResults();\n                for (GeocoderResult result : results) {\n                    location = result.getFormattedAddress();\n                    break;\n                }\n            }\n        }\n\n        return location;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/service/SecurityService.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.service;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport javafx.scene.control.Alert;\n\nimport java.io.IOException;\n\n/**\n * Class that holds the info about the user logged in.\n *\n * Created by gdiaz on 5/05/2017.\n */\npublic class SecurityService {\n\n    /**\n     * Default user account id, used internally by the application for authentication purposes.\n     */\n    private static final String DEFAULT_ACCOUNT_ID = \"Google-Calendar\";\n\n    private static SecurityService instance;\n\n    public static SecurityService getInstance() {\n        if (instance == null) {\n            instance = new SecurityService();\n        }\n        return instance;\n    }\n\n    private GoogleAccount account;\n\n    public GoogleAccount getLoggedAccount() {\n        return account;\n    }\n\n    public boolean isLoggedIn() {\n        return account != null;\n    }\n\n    public boolean isAuthorized() {\n        return GoogleConnector.getInstance().isAuthorized(SecurityService.DEFAULT_ACCOUNT_ID);\n    }\n\n    public boolean authorize(String authorizationCode) {\n        try {\n            GoogleConnector.getInstance().authorize(SecurityService.DEFAULT_ACCOUNT_ID, authorizationCode);\n            return true;\n        } catch (IOException e) {\n            Alert alert = new Alert(Alert.AlertType.ERROR);\n            alert.setHeaderText(\"Unexpected error while authenticating into Google.\");\n            alert.setContentText(e.getLocalizedMessage());\n            alert.show();\n            return false;\n        }\n    }\n\n    public GoogleAccount login() {\n        if (isLoggedIn()) {\n            logout();\n        }\n\n        try {\n            account = GoogleConnector.getInstance().getAccountInfo(SecurityService.DEFAULT_ACCOUNT_ID);\n            return account;\n        } catch (IOException e) {\n            Alert alert = new Alert(Alert.AlertType.ERROR);\n            alert.setHeaderText(\"Unexpected error while login into Google.\");\n            alert.setContentText(e.getLocalizedMessage());\n            alert.show();\n            return null;\n        }\n    }\n\n    public void logout() {\n        account = null;\n        try {\n            GoogleConnector.getInstance().removeCredential(SecurityService.DEFAULT_ACCOUNT_ID);\n        } catch (IOException e) {\n            Alert alert = new Alert(Alert.AlertType.ERROR);\n            alert.setHeaderText(\"Unexpected error removing credentials\");\n            alert.setContentText(e.getLocalizedMessage());\n            alert.show();\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/GoogleCalendarAppView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view;\n\nimport com.calendarfx.google.view.log.LogPane;\nimport com.calendarfx.view.CalendarFXControl;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport impl.com.calendarfx.google.view.GoogleCalendarAppViewSkin;\nimport javafx.scene.control.Skin;\n\nimport java.util.Objects;\n\n/**\n * Control which allows to log in by using a Google account in order to get\n * access to the user calendar data. Displays the Google Login web page and then\n * lets the user authorize us to read/write his calendar information. After\n * authorization this displays a {@link DateControl} configured externally.\n *\n * @author Gabriel Diaz, 14.02.2015.\n */\npublic class GoogleCalendarAppView extends CalendarFXControl {\n\n    private final CalendarView calendarView;\n\n    private final LogPane logPane;\n\n    public GoogleCalendarAppView(CalendarView calendarView) {\n        super();\n        this.calendarView = Objects.requireNonNull(calendarView);\n        this.logPane = new LogPane();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new GoogleCalendarAppViewSkin(this);\n    }\n\n    public CalendarView getCalendarView() {\n        return calendarView;\n    }\n\n    public LogPane getLogPane() {\n        return logPane;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/data/GoogleCalendarData.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.data;\n\nimport com.calendarfx.google.model.GoogleEntry;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.TreeSet;\n\n/**\n * Class storing data about the entries loaded from google for one single calendar.\n *\n * Created by gdiaz on 27/02/2017.\n */\npublic final class GoogleCalendarData {\n\n    /**\n     * Set of Ids already loaded.\n     */\n    private final Set<String> loadedEntryIds = new HashSet<>();\n\n    /**\n     * Set of searchText already loaded.\n     */\n    private final Set<String> loadedSearchText = new HashSet<>();\n\n    /**\n     * Set of slices already loaded for this calendar.\n     */\n    private final Set<Slice> loadedSlices = new TreeSet<>();\n\n    /**\n     * Set of slices being loaded in background.\n     */\n    private final Set<Slice> inProgressSlices = new TreeSet<>();\n\n    /**\n     * Takes the list of slices and removes those already loaded.\n     *\n     * @param slices the slices to be processed.\n     * @return A new list containing the unloaded slices.\n     */\n    public List<Slice> getUnloadedSlices(List<Slice> slices) {\n        List<Slice> unloadedSlices = new ArrayList<>(slices);\n        unloadedSlices.removeAll(loadedSlices);\n        unloadedSlices.removeAll(inProgressSlices);\n        return unloadedSlices;\n    }\n\n    /**\n     * Adds the given list of slices to the ones being loaded.  This produces the slices are not classified as not loaded but in progress loading.\n     *\n     * @param slices The list of slices to be added.\n     */\n    public void addInProgressSlices(List<Slice> slices) {\n        inProgressSlices.addAll(slices);\n    }\n\n    /**\n     * Adds the given slice to the list of loaded ones.  This also removes it from the list of being loaded ones, in case it was added.\n     *\n     * @param slice The slice to be added.\n     */\n    public void addLoadedSlice(Slice slice) {\n        loadedSlices.add(slice);\n        inProgressSlices.remove(slice);\n    }\n\n    /**\n     * Checks whether an entry was already added or not.\n     *\n     * @param entry The entry to be loaded.\n     * @return True or false.\n     */\n    public boolean isLoadedEntry(GoogleEntry entry) {\n        return loadedEntryIds.contains(entry.getId());\n    }\n\n    /**\n     * Puts the entry in the loaded ones.\n     *\n     * @param entry The entry loaded.\n     */\n    public void addLoadedEntry(GoogleEntry entry) {\n        loadedEntryIds.add(entry.getId());\n    }\n\n    /**\n     * Checks whether the search text was already loaded or not.\n     *\n     * @param searchText The search text.\n     * @return True or false.\n     */\n    public boolean isLoadedSearchText(String searchText) {\n        return loadedSearchText.contains(searchText);\n    }\n\n    /**\n     * Puts the text in the loaded ones.\n     *\n     * @param searchText The text to be added.\n     */\n    public void addLoadedSearchText(String searchText) {\n        loadedSearchText.add(searchText);\n    }\n\n    /**\n     * Clears the data stored about the calendar.\n     */\n    public void clear() {\n        loadedSlices.clear();\n        loadedSearchText.clear();\n        loadedEntryIds.clear();\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/data/IGoogleCalendarDataProvider.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.data;\n\nimport com.calendarfx.google.model.GoogleCalendar;\n\n/**\n * Provider of the google calendar data.\n *\n * Created by gdiaz on 5/05/2017.\n */\npublic interface IGoogleCalendarDataProvider {\n\n    default GoogleCalendarData getCalendarData(GoogleCalendar calendar) {\n        return getCalendarData(calendar, false);\n    }\n\n    GoogleCalendarData getCalendarData(GoogleCalendar calendar, boolean create);\n\n    void removeCalendarData(GoogleCalendar calendar);\n\n    void clearData();\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/data/Slice.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.data;\n\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.Lists;\n\nimport java.time.LocalDate;\nimport java.time.YearMonth;\nimport java.time.temporal.ChronoUnit;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Class representing a period for loading events from google.\n *\n * @author Gabriel Diaz, 20.03.2015.\n */\npublic final class Slice implements Comparable {\n\n    private final LocalDate start;\n    private final LocalDate end;\n\n    private Slice(LocalDate start, LocalDate end) {\n        this.start = Objects.requireNonNull(start);\n        this.end = Objects.requireNonNull(end);\n    }\n\n    public LocalDate getStart() {\n        return start;\n    }\n\n    public LocalDate getEnd() {\n        return end;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((start == null) ? 0 : start.hashCode());\n        result = prime * result + ((end == null) ? 0 : end.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n\n        if (obj == null || getClass() != obj.getClass()) {\n            return false;\n        }\n\n        Slice other = (Slice) obj;\n        if (start == null) {\n            if (other.start != null) {\n                return false;\n            }\n        } else if (!start.equals(other.start)) {\n            return false;\n        }\n\n        if (end == null) {\n            return other.end == null;\n        } else return end.equals(other.end);\n    }\n\n    @Override\n    public String toString() {\n        return \"Slice [\" + getStart() + \" | \" + getEnd() + \"]\";\n    }\n\n    @Override\n    public int compareTo(Object o) {\n        if (o instanceof Slice) {\n            Slice other = (Slice) o;\n            if (start == null) {\n                return other.start == null ? 0 : -1;\n            }\n            return start.compareTo(other.start);\n        }\n        return 1;\n    }\n\n    /**\n     * Splits the given period into multiple slices of one month long.\n     *\n     * @param start the start of the period.\n     * @param end   the end of the period.\n     * @return The list of slices result of the splitting.\n     */\n    public static List<Slice> split(LocalDate start, LocalDate end) {\n        Objects.requireNonNull(start);\n        Objects.requireNonNull(end);\n        Preconditions.checkArgument(!start.isAfter(end));\n\n        List<Slice> slices = Lists.newArrayList();\n\n        LocalDate startOfMonth = start.withDayOfMonth(1);\n        LocalDate endOfMonth = YearMonth.from(end).atEndOfMonth();\n\n        do {\n            slices.add(new Slice(startOfMonth, YearMonth.from(startOfMonth).atEndOfMonth()));\n            startOfMonth = startOfMonth.plus(1, ChronoUnit.MONTHS);\n        }\n        while (startOfMonth.isBefore(endOfMonth) || startOfMonth.isEqual(endOfMonth));\n\n        return slices;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/log/ActionType.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.log;\n\n/**\n * Action performed by the user.\n *\n * Created by gdiaz on 28/02/2017.\n */\npublic enum ActionType {\n\n    LOAD {\n        @Override\n        public String getDisplayName() {\n            return \"Load\";\n        }\n    },\n\n    INSERT {\n        @Override\n        public String getDisplayName() {\n            return \"Insert\";\n        }\n    },\n\n    UPDATE {\n        @Override\n        public String getDisplayName() {\n            return \"Update\";\n        }\n    },\n\n    DELETE {\n        @Override\n        public String getDisplayName() {\n            return \"Delete\";\n        }\n    },\n\n    MOVE {\n        @Override\n        public String getDisplayName() {\n            return \"Move\";\n        }\n    },\n\n    REFRESH {\n        @Override\n        public String getDisplayName() {\n            return \"Refresh\";\n        }\n    };\n\n    public abstract String getDisplayName();\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/log/LogItem.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.log;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\n\nimport java.time.LocalDateTime;\n\n/**\n * Item of the log that provides information about one performed task.\n *\n * Created by gdiaz on 22/02/2017.\n */\npublic class LogItem {\n\n    private final ObjectProperty<LocalDateTime> time = new SimpleObjectProperty<>(this, \"time\");\n\n    public final ObjectProperty<LocalDateTime> timeProperty() {\n        return time;\n    }\n\n    public final LocalDateTime getTime() {\n        return timeProperty().get();\n    }\n\n    public final void setTime(LocalDateTime time) {\n        timeProperty().set(time);\n    }\n\n    private final ObjectProperty<ActionType> action = new SimpleObjectProperty<>(this, \"action\");\n\n    public final ObjectProperty<ActionType> actionProperty() {\n        return action;\n    }\n\n    public ActionType getAction() {\n        return actionProperty().get();\n    }\n\n    public void setAction(ActionType action) {\n        actionProperty().set(action);\n    }\n\n    private final StringProperty calendar = new SimpleStringProperty(this, \"calendar\");\n\n    public final StringProperty calendarProperty() {\n        return calendar;\n    }\n\n    public final String getCalendar() {\n        return calendarProperty().get();\n    }\n\n    public final void setCalendar(String calendar) {\n        calendarProperty().set(calendar);\n    }\n\n    private final ObjectProperty<Throwable> exception = new SimpleObjectProperty<>(this, \"exception\");\n\n    public final ObjectProperty<Throwable> exceptionProperty() {\n        return exception;\n    }\n\n    public final Throwable getException() {\n        return exceptionProperty().get();\n    }\n\n    public final void setException(Throwable exception) {\n        exceptionProperty().set(exception);\n    }\n\n    private final StringProperty description = new SimpleStringProperty(this, \"description\");\n\n    public final StringProperty descriptionProperty() {\n        return description;\n    }\n\n    public final String getDescription() {\n        return descriptionProperty().get();\n    }\n\n    public final void setDescription(String description) {\n        descriptionProperty().set(description);\n    }\n\n    private final ObjectProperty<StatusType> status = new SimpleObjectProperty<>(this, \"status\");\n\n    public final ObjectProperty<StatusType> statusProperty() {\n        return status;\n    }\n\n    public final StatusType getStatus() {\n        return statusProperty().get();\n    }\n\n    public final void setStatus(StatusType status) {\n        statusProperty().set(status);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/log/LogPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.log;\n\nimport impl.com.calendarfx.google.view.log.LogPaneSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.FilteredList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Control;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.control.cell.PropertyValueFactory;\nimport javafx.scene.paint.Color;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.Collection;\nimport java.util.function.Predicate;\n\n/**\n * Pane that displays a table with all log items registered by the app.\n *\n * Created by gdiaz on 22/02/2017.\n */\npublic class LogPane extends Control {\n\n    private final ObservableList<LogItem> items = FXCollections.observableArrayList();\n    private final FilteredList filteredData;\n    private final TableView<LogItem> table;\n    private LogTableFilter filter;\n\n    public LogPane() {\n        super();\n        table = new TableView<>();\n\n        TableColumn<LogItem, StatusType> statusColumn = new TableColumn<>(\"Status\");\n        statusColumn.setCellValueFactory(new PropertyValueFactory<>(\"status\"));\n        statusColumn.prefWidthProperty().bind(Bindings.multiply(0.1, table.widthProperty()));\n        statusColumn.setCellFactory(col -> new StatusTypeCell());\n\n        TableColumn<LogItem, ActionType> actionColumn = new TableColumn<>(\"Action\");\n        actionColumn.setCellValueFactory(new PropertyValueFactory<>(\"action\"));\n        actionColumn.prefWidthProperty().bind(Bindings.multiply(0.1, table.widthProperty()));\n        actionColumn.setCellFactory(col -> new ActionTypeCell());\n\n        TableColumn<LogItem, LocalDateTime> timeColumn = new TableColumn<>(\"Time\");\n        timeColumn.setCellValueFactory(new PropertyValueFactory<>(\"time\"));\n        timeColumn.prefWidthProperty().bind(Bindings.multiply(0.2, table.widthProperty()));\n        timeColumn.setCellFactory(col -> new TimeCell());\n\n        TableColumn<LogItem, String> calendarColumn = new TableColumn<>(\"Calendar\");\n        calendarColumn.setCellValueFactory(new PropertyValueFactory<>(\"calendar\"));\n        calendarColumn.prefWidthProperty().bind(Bindings.multiply(0.2, table.widthProperty()));\n\n        TableColumn<LogItem, String> descriptionColumn = new TableColumn<>(\"Description\");\n        descriptionColumn.setCellValueFactory(new PropertyValueFactory<>(\"description\"));\n        descriptionColumn.prefWidthProperty().bind(Bindings.multiply(0.4, table.widthProperty()));\n\n        filteredData = new FilteredList<>(items);\n        SortedList<LogItem> sortedData = new SortedList<>(filteredData);\n        sortedData.comparatorProperty().bind(table.comparatorProperty());\n\n        table.getColumns().add(statusColumn);\n        table.getColumns().add(actionColumn);\n        table.getColumns().add(timeColumn);\n        table.getColumns().add(calendarColumn);\n        table.getColumns().add(descriptionColumn);\n        table.setTableMenuButtonVisible(true);\n        table.setItems(sortedData);\n        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new LogPaneSkin(this, table);\n    }\n\n    public final ObservableList<LogItem> getItems() {\n        return items;\n    }\n\n    public final void clearItems() {\n        items.clear();\n        table.getSelectionModel().clearSelection();\n    }\n\n    public final void removeItems(Collection<LogItem> items) {\n        this.items.removeAll(items);\n        table.getSelectionModel().clearSelection();\n    }\n\n    public final ObservableList<LogItem> getSelectedItems() {\n        return table.getSelectionModel().getSelectedItems();\n    }\n\n    public final void filter(Collection<StatusType> statuses) {\n        filter = new LogTableFilter(filter);\n        filter.setStatusTypes(statuses);\n        filteredData.setPredicate(filter);\n    }\n\n    public final void filter(String text) {\n        filter = new LogTableFilter(filter);\n        filter.setText(text);\n        filteredData.setPredicate(filter);\n    }\n\n    public final void filter(ActionType actionType) {\n        filter = new LogTableFilter(filter);\n        filter.setActionType(actionType);\n        filteredData.setPredicate(filter);\n    }\n\n    /**\n     * Filter used for the table.\n     */\n    private static class LogTableFilter implements Predicate<LogItem> {\n\n        private Collection<StatusType> statuses;\n        private String text;\n        private ActionType actionType;\n\n        LogTableFilter(LogTableFilter oldFilter) {\n            if (oldFilter != null) {\n                this.statuses = oldFilter.statuses;\n                this.text = oldFilter.text;\n                this.actionType = oldFilter.actionType;\n            }\n        }\n\n        void setStatusTypes(Collection<StatusType> statuses) {\n            this.statuses = statuses;\n        }\n\n        void setText(String text) {\n            this.text = text;\n        }\n\n        void setActionType(ActionType actionType) {\n            this.actionType = actionType;\n        }\n\n        @Override\n        public boolean test(LogItem logItem) {\n            if (statuses != null && !statuses.contains(logItem.getStatus())) {\n                return false;\n            }\n\n            if (actionType != null && !actionType.equals(logItem.getAction())) {\n                return false;\n            }\n\n            if (text != null && !text.isEmpty()) {\n                String textLower = text.toLowerCase();\n\n                if (logItem.getDescription() != null) {\n                    String descriptionLower = logItem.getDescription().toLowerCase();\n                    if (descriptionLower.contains(textLower)) {\n                        return true;\n                    }\n                }\n\n                if (logItem.getCalendar() != null) {\n                    String calendarLower = logItem.getCalendar().toLowerCase();\n                    return calendarLower.contains(textLower);\n                }\n\n                return false;\n            }\n\n            return true;\n        }\n    }\n\n    /**\n     * Cell for the status column.\n     */\n    private static class StatusTypeCell extends TableCell<LogItem, StatusType> {\n        @Override\n        protected void updateItem(StatusType item, boolean empty) {\n            super.updateItem(item, empty);\n            setText(null);\n            setTooltip(null);\n            setAlignment(Pos.CENTER);\n            setGraphic(null);\n            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n            if (item != null && !empty) {\n                setGraphic(item.createView());\n                setTooltip(new Tooltip(item.getDisplayName()));\n            }\n        }\n    }\n\n    /**\n     * Cell for the action type column.\n     */\n    private static class ActionTypeCell extends TableCell<LogItem, ActionType> {\n        @Override\n        protected void updateItem(ActionType item, boolean empty) {\n            super.updateItem(item, empty);\n            setText(null);\n            setTextFill(Color.BLACK);\n            if (item != null && !empty) {\n                setText(item.getDisplayName());\n            }\n        }\n    }\n\n    /**\n     * Cell for the time column.\n     */\n    private static class TimeCell extends TableCell<LogItem, LocalDateTime> {\n\n        private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT);\n\n        @Override\n        protected void updateItem(LocalDateTime item, boolean empty) {\n            super.updateItem(item, empty);\n            setText(null);\n            if (item != null && !empty) {\n                setText(FORMATTER.format(item));\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/log/StatusType.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.log;\n\nimport javafx.scene.paint.Color;\nimport org.kordamp.ikonli.Ikon;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\n/**\n * Status of the google task.\n *\n * Created by gdiaz on 28/02/2017.\n */\npublic enum StatusType {\n\n    SUCCEEDED {\n        @Override\n        public String getDisplayName() {\n            return \"Succeeded\";\n        }\n\n        @Override\n        public Ikon getIcon() {\n            return FontAwesome.CHECK;\n        }\n\n        @Override\n        public Color getColor() {\n            return Color.GREEN;\n        }\n    },\n\n    PENDING {\n        @Override\n        public String getDisplayName() {\n            return \"Pending\";\n        }\n\n        @Override\n        public Ikon getIcon() {\n            return FontAwesome.EXCLAMATION_TRIANGLE;\n        }\n\n        @Override\n        public Color getColor() {\n            return Color.ORANGE;\n        }\n    },\n\n    FAILED {\n        @Override\n        public String getDisplayName() {\n            return \"Failed\";\n        }\n\n        @Override\n        public Ikon getIcon() {\n            return FontAwesome.CLOSE;\n        }\n\n        @Override\n        public Color getColor() {\n            return Color.RED;\n        }\n    },\n\n    CANCELLED {\n        @Override\n        public String getDisplayName() {\n            return \"Cancelled\";\n        }\n\n        @Override\n        public Ikon getIcon() {\n            return FontAwesome.EXCLAMATION_CIRCLE;\n        }\n\n        @Override\n        public Color getColor() {\n            return Color.BLUE;\n        }\n    };\n\n    public abstract String getDisplayName();\n\n    public abstract Ikon getIcon();\n\n    public abstract Color getColor();\n\n    public FontIcon createView() {\n        FontIcon view = new FontIcon(getIcon());\n        view.setFill(getColor());\n        return view;\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/popover/GoogleEntryAttendeesView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.popover;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.view.popover.EntryPopOverPane;\nimport com.google.api.services.calendar.model.EventAttendee;\nimport com.google.common.collect.Lists;\nimport javafx.beans.Observable;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.css.PseudoClass;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\nimport org.controlsfx.glyphfont.FontAwesome;\nimport org.controlsfx.glyphfont.Glyph;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport static java.util.Objects.requireNonNull;\nimport static javafx.scene.input.ContextMenuEvent.CONTEXT_MENU_REQUESTED;\n\n/**\n * Pane used to edit the attendees of a Google Entry.\n *\n * @author Gabriel Diaz, 21.02.2015.\n */\npublic class GoogleEntryAttendeesView extends EntryPopOverPane {\n\n    private static final PseudoClass INVALID = PseudoClass.getPseudoClass(\"invalid\");\n\n    private final GoogleEntry entry;\n    private final TextField txtEmail;\n    private final Button btAdd;\n\n    public GoogleEntryAttendeesView(GoogleEntry entry) {\n        super();\n\n        this.entry = requireNonNull(entry);\n\n        txtEmail = new TextField();\n        txtEmail.getStyleClass().add(\"email-field\");\n        txtEmail.textProperty().addListener(obs -> enableButton());\n        txtEmail.setOnAction(evt -> createAttendee());\n\n        btAdd = new Button(\"Add\");\n        btAdd.setOnAction(evt -> createAttendee());\n\n        enableButton();\n\n        Label lblTitle = new Label(\"Attendees can:\");\n        lblTitle.getStyleClass().add(\"title\");\n\n        CheckBox chkEdit = new CheckBox(\"modify event\");\n        chkEdit.selectedProperty().bindBidirectional(entry.attendeesCanModifyProperty());\n        chkEdit.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        CheckBox chkInvite = new CheckBox(\"invite others\");\n        chkInvite.selectedProperty().bindBidirectional(entry.attendeesCanInviteOthersProperty());\n        chkInvite.disableProperty().bind(Bindings.or(entry.getCalendar().readOnlyProperty(), entry.attendeesCanModifyProperty()));\n\n        CheckBox chkSeeOthers = new CheckBox(\"see attendees list\");\n        chkSeeOthers.selectedProperty().bindBidirectional(entry.attendeesCanSeeOthersProperty());\n        chkSeeOthers.disableProperty().bind(Bindings.or(entry.getCalendar().readOnlyProperty(), entry.attendeesCanModifyProperty()));\n\n        HBox checksParent = new HBox(chkEdit, chkInvite, chkSeeOthers);\n        checksParent.getStyleClass().add(\"checks-parent\");\n\n        HBox.setHgrow(txtEmail, Priority.ALWAYS);\n        HBox.setHgrow(btAdd, Priority.NEVER);\n\n        HBox.setHgrow(chkEdit, Priority.ALWAYS);\n        HBox.setHgrow(chkInvite, Priority.ALWAYS);\n        HBox.setHgrow(chkSeeOthers, Priority.ALWAYS);\n\n        HBox top = new HBox(txtEmail, btAdd);\n        top.getStyleClass().add(\"top\");\n        VBox center = new VBox();\n        center.getStyleClass().add(\"center\");\n        VBox bottom = new VBox(lblTitle, checksParent);\n        bottom.getStyleClass().add(\"bottom\");\n\n        BorderPane container = new BorderPane();\n        container.setTop(top);\n        container.setCenter(center);\n        container.setBottom(bottom);\n\n        getChildren().add(container);\n        getStyleClass().add(\"attendees-view\");\n\n        entry.getAttendees().addListener((Observable obs) -> buildItems(center, entry.getAttendees()));\n        buildItems(center, entry.getAttendees());\n    }\n\n    private void createAttendee() {\n        createAttendee(txtEmail.getText());\n        txtEmail.setText(\"\");\n        enableButton();\n        getScene().getWindow().sizeToScene();\n    }\n\n    private void enableButton() {\n        txtEmail.pseudoClassStateChanged(INVALID, false);\n\n        if (entry.getCalendar().isReadOnly()) {\n            btAdd.setDisable(true);\n            return;\n        }\n\n        if (txtEmail.getText() == null || txtEmail.getText().trim().isEmpty()) {\n            btAdd.setDisable(true);\n            return;\n        }\n\n        if (!isValidEmail(txtEmail.getText())) {\n            btAdd.setDisable(true);\n            txtEmail.pseudoClassStateChanged(INVALID, true);\n            return;\n        }\n\n        for (EventAttendee attendee : entry.getAttendees()) {\n            if (attendee.getEmail().equals(txtEmail.getText())) {\n                btAdd.setDisable(true);\n                return;\n            }\n        }\n\n        btAdd.setDisable(false);\n    }\n\n    private boolean isValidEmail(String email) {\n        String ePattern = \"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\\\[[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\])|(([a-zA-Z\\\\-0-9]+\\\\.)+[a-zA-Z]{2,}))$\";\n        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(ePattern);\n        java.util.regex.Matcher matcher = pattern.matcher(email);\n        return matcher.matches();\n    }\n\n    private void buildItems(VBox parent, List<EventAttendee> attendees) {\n        List<GoogleEntryAttendeeItem> attendeesNode = Lists.newArrayList();\n        for (EventAttendee attendee : attendees) {\n            attendeesNode.add(new GoogleEntryAttendeeItem(attendee));\n        }\n        parent.getChildren().setAll(attendeesNode);\n    }\n\n    private void createAttendee(String email) {\n        EventAttendee attendee = new EventAttendee();\n        attendee.setEmail(email);\n        attendee.setDisplayName(email);\n        entry.getAttendees().add(attendee);\n    }\n\n    private void removeAttendee(EventAttendee attendee) {\n        entry.getAttendees().remove(attendee);\n    }\n\n    /**\n     * Custom control to display an attendee of the Google Entry.\n     *\n     * @author Gabriel Diaz, 21.02.2015.\n     */\n    private class GoogleEntryAttendeeItem extends HBox {\n\n        private final Label optionalIcon;\n        private final Label statusIcon;\n        private final Label name;\n        private final Label removeButton;\n        private final EventAttendee attendee;\n\n        public GoogleEntryAttendeeItem(EventAttendee attendee) {\n            this.attendee = Objects.requireNonNull(attendee);\n\n            optionalIcon = new Label();\n            optionalIcon.setOnMouseClicked(evt -> setOptional(!isOptional()));\n            optionalIcon.getStyleClass().add(\"button-icon\");\n            optionalIcon.setTooltip(new Tooltip());\n\n            statusIcon = new Label();\n\n            name = new Label();\n            name.setMaxWidth(Double.MAX_VALUE);\n\n            setOptional(Boolean.TRUE.equals(attendee.getOptional()));\n            optionalProperty().addListener(obs -> updateIcon());\n            updateIcon();\n\n            removeButton = new Label();\n            removeButton.setGraphic(new FontAwesome().create(FontAwesome.Glyph.TRASH_ALT));\n            removeButton.getStyleClass().add(\"button-icon\");\n            removeButton.setOnMouseClicked(evt -> removeAttendee(attendee));\n\n            HBox.setHgrow(optionalIcon, Priority.NEVER);\n            HBox.setHgrow(name, Priority.ALWAYS);\n            HBox.setHgrow(removeButton, Priority.NEVER);\n\n            getStyleClass().add(\"attendee-item\");\n            getChildren().addAll(optionalIcon, statusIcon, name, removeButton);\n\n            ContextMenu menu = new ContextMenu();\n            MenuItem optionalItem = new MenuItem(\"Mark as optional\");\n            optionalItem.setOnAction(evt -> setOptional(true));\n            MenuItem requiredItem = new MenuItem(\"Mark as required\");\n            requiredItem.setOnAction(evt -> setOptional(false));\n            MenuItem removeItem = new MenuItem(\"Remove attendee\");\n            removeItem.setOnAction(evt -> removeAttendee(attendee));\n            menu.getItems().addAll(optionalItem, requiredItem, new SeparatorMenuItem(), removeItem);\n\n            addEventHandler(CONTEXT_MENU_REQUESTED, evt -> menu.show(this, evt.getScreenX(), evt.getScreenY()));\n        }\n\n        private void updateIcon() {\n            FontAwesome fontAwesome = new FontAwesome();\n            Glyph img = fontAwesome.create(FontAwesome.Glyph.MALE);\n            img.setOpacity(isOptional() ? 0.4 : 1.0);\n            optionalIcon.setGraphic(img);\n            optionalIcon.getTooltip().setText(isOptional() ? \"optional\" : \"required\");\n            name.setText(attendee.getEmail() + (isOptional() ? \" (optional)\" : \"\"));\n        }\n\n        private final BooleanProperty optional = new SimpleBooleanProperty(this, \"optional\");\n\n        public final BooleanProperty optionalProperty() {\n            return optional;\n        }\n\n        public final boolean isOptional() {\n            return optionalProperty().get();\n        }\n\n        public final void setOptional(boolean optional) {\n            optionalProperty().set(optional);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/popover/GoogleEntryDetailsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.popover;\n\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.calendarfx.google.model.GoogleEntryReminder.RemindMethod;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.popover.EntryDetailsView;\nimport com.google.common.collect.Lists;\nimport javafx.beans.Observable;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Pos;\nimport javafx.geometry.VPos;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\nimport javafx.util.StringConverter;\nimport org.controlsfx.glyphfont.FontAwesome;\n\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * Custom details view which adds some additional controls to edit google entry\n * specific info.\n *\n * @author Gabriel Diaz, 07.03.2015.\n */\npublic class GoogleEntryDetailsView extends EntryDetailsView {\n\n    private final GoogleEntry entry;\n\n    public GoogleEntryDetailsView(GoogleEntry entry, DateControl dateControl) {\n        super(requireNonNull(entry), dateControl);\n\n        this.entry = entry;\n\n        Label notificationLabel = new Label(\"Notification:\");\n\n        Label addButton = new Label(\"Add a notification\");\n        addButton.getStyleClass().add(\"link\");\n        addButton.setOnMouseClicked(evt -> createReminder());\n        addButton.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        VBox center = new VBox();\n\n        BorderPane notificationPane = new BorderPane();\n        notificationPane.setCenter(center);\n        notificationPane.setBottom(addButton);\n\n        GridPane box = (GridPane) getChildren().get(0);\n        box.add(notificationLabel, 0, 5);\n        box.add(notificationPane, 1, 5);\n\n        GridPane.setValignment(notificationLabel, VPos.TOP);\n        getStyleClass().add(\"details-view\");\n\n        if (entry.isUseDefaultReminder()) {\n            GoogleCalendar calendar = (GoogleCalendar) entry.getCalendar();\n            reminders.addAll(calendar.getDefaultReminders());\n        }\n\n        reminders.addAll(entry.getReminders());\n        reminders.addListener((Observable obs) -> buildItems(center));\n        buildItems(center);\n    }\n\n    private final ObservableList<GoogleEntryReminder> reminders = FXCollections.observableArrayList();\n\n    private void buildItems(VBox parent) {\n        List<GoogleEntryReminderItem> attendeesNode = Lists.newArrayList();\n        for (GoogleEntryReminder reminder : reminders) {\n            attendeesNode.add(new GoogleEntryReminderItem(reminder));\n        }\n        parent.getChildren().setAll(attendeesNode);\n    }\n\n    private void createReminder() {\n        GoogleEntryReminder reminder = new GoogleEntryReminder();\n        reminder.setMethod(RemindMethod.POPUP);\n        reminder.setMinutes(10);\n        reminders.add(reminder);\n        entry.getReminders().add(reminder);\n    }\n\n    private void removeReminder(GoogleEntryReminder reminder) {\n        reminders.remove(reminder);\n        entry.getReminders().remove(reminder);\n    }\n\n    /**\n     * Control that represents an event reminder item.\n     *\n     * @author Gabriel Diaz, 07.03.2015.\n     */\n    private class GoogleEntryReminderItem extends HBox {\n\n        private final GoogleEntryReminder reminder;\n        private final ComboBox<RemindMethod> methodCombo;\n        private final ComboBox<TimeUnit> unitCombo;\n        private final TextField valueTxt;\n        private final Label removeIcon;\n\n        private GoogleEntryReminderItem(GoogleEntryReminder reminder) {\n            this.reminder = requireNonNull(reminder);\n\n            methodCombo = new ComboBox<>();\n            methodCombo.getItems().setAll(RemindMethod.values());\n            methodCombo.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n            methodCombo.valueProperty().bindBidirectional(reminder.methodProperty());\n            methodCombo.setConverter(new StringConverter<RemindMethod>() {\n                @Override\n                public String toString(RemindMethod object) {\n                    return object.getName();\n                }\n\n                @Override\n                public RemindMethod fromString(String string) {\n                    for (RemindMethod method : RemindMethod.values()) {\n                        if (method.getName().equals(string)) {\n                            return method;\n                        }\n                    }\n                    return null;\n                }\n            });\n\n            Integer minutes = reminder.getMinutes();\n            TimeUnit unit = TimeUnit.MINUTES;\n            if (minutes != null) {\n                if (minutes % 1440 == 0) {\n                    unit = TimeUnit.DAYS;\n                    minutes = minutes / 1400;\n                } else if (minutes % 60 == 0) {\n                    unit = TimeUnit.HOURS;\n                    minutes = minutes / 60;\n                }\n            }\n\n            valueTxt = new TextField();\n            valueTxt.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n            valueTxt.setPrefColumnCount(5);\n            valueTxt.setText(minutes == null ? \"\" : minutes.toString());\n            valueTxt.textProperty().addListener(obs -> updateMinutes());\n\n            unitCombo = new ComboBox<>();\n            unitCombo.getItems().setAll(TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS);\n            unitCombo.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n            unitCombo.setValue(unit);\n            unitCombo.valueProperty().addListener(obs -> updateMinutes());\n\n            removeIcon = new Label();\n            removeIcon.getStyleClass().add(\"button-icon\");\n            removeIcon.setGraphic(new FontAwesome().create(FontAwesome.Glyph.TRASH_ALT));\n            removeIcon.setOnMouseClicked(evt -> removeReminder(reminder));\n            removeIcon.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n            HBox.setHgrow(removeIcon, Priority.NEVER);\n            setAlignment(Pos.CENTER_LEFT);\n            getChildren().addAll(methodCombo, valueTxt, unitCombo, removeIcon);\n            getStyleClass().add(\"notification-item\");\n        }\n\n        private void updateMinutes() {\n            Integer minutes = null;\n\n            try {\n                Integer value = Integer.valueOf(valueTxt.getText());\n\n                if (unitCombo.getValue() == TimeUnit.DAYS) {\n                    minutes = Math.toIntExact(TimeUnit.DAYS.toMinutes(value));\n                } else if (unitCombo.getValue() == TimeUnit.HOURS) {\n                    minutes = Math.toIntExact(TimeUnit.HOURS.toMinutes(value));\n                } else {\n                    minutes = value;\n                }\n            } catch (NumberFormatException e) {\n                // DO nothing\n            }\n\n            reminder.setMinutes(minutes);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/popover/GoogleEntryGMapsFXView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.popover;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.view.popover.EntryPopOverPane;\nimport com.google.code.geocoder.model.GeocoderGeometry;\nimport com.dlsc.gmapsfx.GoogleMapView;\nimport com.dlsc.gmapsfx.MapComponentInitializedListener;\nimport com.dlsc.gmapsfx.javascript.object.GoogleMap;\nimport com.dlsc.gmapsfx.javascript.object.LatLong;\nimport com.dlsc.gmapsfx.javascript.object.MapOptions;\nimport com.dlsc.gmapsfx.javascript.object.MapTypeIdEnum;\nimport com.dlsc.gmapsfx.javascript.object.Marker;\nimport com.dlsc.gmapsfx.javascript.object.MarkerOptions;\nimport javafx.application.Platform;\nimport javafx.concurrent.Service;\nimport javafx.concurrent.Task;\nimport javafx.scene.layout.StackPane;\n\n/**\n * Pane that shows in a map the location of the entry.\n *\n * Created by gdiaz on 13/01/2017.\n */\npublic class GoogleEntryGMapsFXView extends EntryPopOverPane implements MapComponentInitializedListener {\n\n    private final GoogleEntry entry;\n    private final GoogleMapView mapView = new GoogleMapView();\n    private final StackPane mapViewWrapper = new StackPane();\n    private final LocationFXService service = new LocationFXService();\n\n    GoogleEntryGMapsFXView(GoogleEntry entry) {\n        this.entry = entry;\n        this.entry.locationProperty().addListener(obs -> updateLocation());\n\n        this.mapView.addMapInitializedListener(this);\n        this.mapView.getStyleClass().add(\"map\");\n        this.mapView.setMouseTransparent(true);\n\n        this.mapViewWrapper.getChildren().add(mapView);\n        this.mapViewWrapper.setVisible(false);\n        this.mapViewWrapper.managedProperty().bind(this.mapView.visibleProperty());\n        this.mapViewWrapper.setPrefSize(300, 300);\n\n        StackPane stackPane = new StackPane();\n        stackPane.getStyleClass().add(\"map-view-wrapper\");\n        stackPane.getChildren().add(mapViewWrapper);\n        getChildren().add(stackPane);\n    }\n\n    @Override\n    public void mapInitialized() {\n        Platform.runLater(this::updateLocation);\n    }\n\n    private GoogleMap createMap() {\n        MapOptions options = new MapOptions();\n        options.zoom(15)\n                .overviewMapControl(false)\n                .mapTypeControl(false)\n                .panControl(false)\n                .rotateControl(false)\n                .scaleControl(false)\n                .streetViewControl(false)\n                .zoomControl(false)\n                .mapType(MapTypeIdEnum.ROADMAP);\n        return mapView.createMap(options, false);\n    }\n\n    private void updateLocation() {\n        service.restart();\n    }\n\n    private class LocationFXService extends Service<GeocoderGeometry> {\n\n        @Override\n        protected Task<GeocoderGeometry> createTask() {\n            return new Task<GeocoderGeometry>() {\n                @Override\n                protected GeocoderGeometry call() throws Exception {\n                    Thread.sleep(1000);\n                    return GoogleConnector.getInstance().getGeocoderService().locationToCoordinate(entry.getLocation());\n                }\n            };\n        }\n\n        @Override\n        public void start() {\n            super.start();\n            mapViewWrapper.setVisible(false);\n        }\n\n        @Override\n        protected void failed() {\n            super.failed();\n            mapViewWrapper.setVisible(false);\n        }\n\n        @Override\n        protected void succeeded() {\n            super.succeeded();\n            GeocoderGeometry geometry = getValue();\n\n            if (geometry != null) {\n                LatLong position = new LatLong(geometry.getLocation().getLat().doubleValue(), geometry.getLocation().getLng().doubleValue());\n                MarkerOptions markerOptions = new MarkerOptions();\n                markerOptions.position(position);\n                markerOptions.title(entry.getTitle());\n                Marker marker = new Marker(markerOptions);\n                GoogleMap map = createMap();\n                map.addMarker(marker);\n                map.setCenter(position);\n                map.panTo(position);\n                mapViewWrapper.setVisible(true);\n                Platform.runLater(() -> Platform.runLater(() -> map.setCenter(position)));\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/popover/GoogleEntryPopOverContentPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.popover;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.popover.EntryHeaderView;\nimport com.calendarfx.view.popover.EntryPropertiesView;\nimport com.calendarfx.view.popover.PopOverContentPane;\nimport com.calendarfx.view.popover.PopOverTitledPane;\nimport javafx.collections.ObservableList;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * Custom according Popover used to edit the information available in a google\n * entry. This also allows to move the entry from a calendar to another.\n *\n * @author Gabriel Diaz, 07.03.2015.\n */\npublic class GoogleEntryPopOverContentPane extends PopOverContentPane {\n\n    public GoogleEntryPopOverContentPane(GoogleEntry entry, ObservableList<Calendar> allCalendars, DateControl dateControl) {\n        requireNonNull(entry);\n        getStylesheets().add(GoogleEntryPopOverContentPane.class.getResource(\"google-popover.css\").toExternalForm());\n\n        EntryHeaderView header = new EntryHeaderView(entry, allCalendars);\n        GoogleEntryDetailsView details = new GoogleEntryDetailsView(entry, dateControl);\n        GoogleEntryAttendeesView attendees = new GoogleEntryAttendeesView(entry);\n        GoogleEntryGMapsFXView mapView = new GoogleEntryGMapsFXView(entry);\n\n        PopOverTitledPane detailsPane = new PopOverTitledPane(\"Details\", details);\n        PopOverTitledPane attendeesPane = new PopOverTitledPane(\"Attendees\", attendees);\n\n        if (Boolean.getBoolean(\"calendarfx.developer\")) {\n            EntryPropertiesView properties = new EntryPropertiesView(entry);\n            PopOverTitledPane propertiesPane = new PopOverTitledPane(\"Properties\", properties);\n            propertiesPane.getStyleClass().add(\"no-padding\");\n            getPanes().addAll(detailsPane, attendeesPane, propertiesPane);\n        } else {\n            getPanes().addAll(detailsPane, attendeesPane);\n        }\n\n        setHeader(header);\n        setExpandedPane(detailsPane);\n        setFooter(mapView);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/DeleteEntryTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\n/**\n * Task that deletes one entry from google.\n *\n * Created by gdiaz on 12/03/2017.\n */\npublic final class DeleteEntryTask extends GoogleTask<Boolean> {\n\n    private final GoogleEntry entry;\n    private final GoogleCalendar calendar;\n    private final GoogleAccount account;\n\n    public DeleteEntryTask(GoogleEntry entry, GoogleCalendar calendar, GoogleAccount account) {\n        this.entry = entry;\n        this.calendar = calendar;\n        this.account = account;\n        this.logItem.setCalendar(calendar.getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.DELETE;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Delete \" + entry;\n    }\n\n    @Override\n    protected Boolean call() throws Exception {\n        GoogleConnector.getInstance().getCalendarService(account.getId()).deleteEntry(entry, calendar);\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/GoogleTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.view.log.ActionType;\nimport com.calendarfx.google.view.log.LogItem;\nimport com.calendarfx.google.view.log.StatusType;\nimport javafx.concurrent.Task;\n\nimport java.time.LocalDateTime;\n\n/**\n * Base class for tasks executed for google calendar interaction.\n *\n * Created by gdiaz on 28/02/2017.\n */\npublic abstract class GoogleTask<V> extends Task<V> {\n\n    final LogItem logItem;\n\n    protected GoogleTask() {\n        super();\n        logItem = new LogItem();\n        logItem.setTime(LocalDateTime.now());\n        logItem.setStatus(StatusType.PENDING);\n        logItem.setDescription(getDescription());\n        logItem.setAction(getAction());\n    }\n\n    public LogItem getLogItem() {\n        return logItem;\n    }\n\n    public abstract ActionType getAction();\n\n    public abstract String getDescription();\n\n    @Override\n    protected void failed() {\n        logItem.setStatus(StatusType.FAILED);\n        logItem.setException(getException());\n    }\n\n    @Override\n    protected void cancelled() {\n        logItem.setStatus(StatusType.CANCELLED);\n    }\n\n    @Override\n    protected void succeeded() {\n        logItem.setStatus(StatusType.SUCCEEDED);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/InsertCalendarTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\n/**\n * Task that performs an insert operation into google.\n *\n * Created by gdiaz on 06.03.2017.\n */\npublic final class InsertCalendarTask extends GoogleTask<GoogleCalendar> {\n\n    private final GoogleCalendar calendar;\n    private final GoogleAccount account;\n\n    public InsertCalendarTask(GoogleCalendar calendar, GoogleAccount account) {\n        this.calendar = calendar;\n        this.account = account;\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.INSERT;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Insert \" + calendar;\n    }\n\n    @Override\n    protected GoogleCalendar call() throws Exception {\n        GoogleConnector.getInstance().getCalendarService(account.getId()).insertCalendar(calendar);\n        return calendar;\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        account.getCalendars().add(calendar);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/InsertEntryTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\n/**\n * Task that inserts an entry into google.\n *\n * Created by gdiaz on 12/03/2017.\n */\npublic final class InsertEntryTask extends GoogleTask<GoogleEntry> {\n\n    private final GoogleEntry entry;\n    private final GoogleCalendar calendar;\n    private final GoogleAccount account;\n\n    public InsertEntryTask(GoogleEntry entry, GoogleCalendar calendar, GoogleAccount account) {\n        this.entry = entry;\n        this.calendar = calendar;\n        this.account = account;\n        this.logItem.setCalendar(calendar.getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.INSERT;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Insert \" + entry;\n    }\n\n    @Override\n    protected GoogleEntry call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).insertEntry(entry, calendar);\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        calendar.addEntries(entry);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/LoadAllCalendarsTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\nimport java.util.List;\n\n/**\n * Task that queries all calendars from google and updates the google calendar source.\n *\n * Created by gdiaz on 28/02/2017.\n */\npublic final class LoadAllCalendarsTask extends GoogleTask<List<GoogleCalendar>> {\n\n    private final GoogleAccount account;\n\n    public LoadAllCalendarsTask(GoogleAccount account) {\n        super();\n        this.account = account;\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.LOAD;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Loading all calendars\";\n    }\n\n    @Override\n    protected List<GoogleCalendar> call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).getCalendars();\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        account.getCalendars().setAll(getValue());\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/LoadEntriesBySliceTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.data.GoogleCalendarData;\nimport com.calendarfx.google.view.data.Slice;\nimport com.calendarfx.google.view.log.ActionType;\n\nimport java.time.ZoneId;\nimport java.util.List;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\n/**\n * Task that loads some entries from google for one single calendar using the period provided by some slices.\n *\n * Created by gdiaz on 6/03/2017.\n */\npublic final class LoadEntriesBySliceTask extends GoogleTask<List<GoogleEntry>> {\n\n    private final GoogleAccount account;\n    private final GoogleCalendar calendar;\n    private final GoogleCalendarData entryData;\n    private final Slice slice;\n    private final ZoneId zoneId;\n\n    public LoadEntriesBySliceTask(GoogleAccount account, GoogleCalendar calendar, GoogleCalendarData entryData, Slice slice, ZoneId zoneId) {\n        super();\n        this.account = checkNotNull(account);\n        this.calendar = checkNotNull(calendar);\n        this.entryData = checkNotNull(entryData);\n        this.slice = checkNotNull(slice);\n        this.zoneId = checkNotNull(zoneId);\n        this.logItem.setCalendar(calendar.getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.LOAD;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Loading \" + slice;\n    }\n\n    @Override\n    protected List<GoogleEntry> call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).getEntries(calendar, slice.getStart(), slice.getEnd(), zoneId);\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        for (GoogleEntry entry : getValue()) {\n            if (!entryData.isLoadedEntry(entry)) {\n                calendar.addEntry(entry);\n                entryData.addLoadedEntry(entry);\n            }\n        }\n        entryData.addLoadedSlice(slice);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/LoadEntriesByTextTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.data.GoogleCalendarData;\nimport com.calendarfx.google.view.log.ActionType;\n\nimport java.util.List;\n\n/**\n * Search entries by text task.\n *\n * Created by gdiaz on 21/03/2017.\n */\npublic final class LoadEntriesByTextTask extends GoogleTask<List<GoogleEntry>> {\n\n    private final String searchText;\n    private final GoogleCalendar calendar;\n    private final GoogleCalendarData data;\n    private final GoogleAccount account;\n\n    public LoadEntriesByTextTask(String searchText, GoogleCalendar calendar, GoogleCalendarData data, GoogleAccount account) {\n        this.searchText = searchText;\n        this.calendar = calendar;\n        this.data = data;\n        this.account = account;\n        this.logItem.setCalendar(calendar.getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.LOAD;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Loading \\\"\" + searchText + \"\\\"\";\n    }\n\n    @Override\n    protected List<GoogleEntry> call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).getEntries(calendar, searchText);\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        for (GoogleEntry entry : getValue()) {\n            if (!data.isLoadedEntry(entry)) {\n                calendar.addEntry(entry);\n                data.addLoadedEntry(entry);\n            }\n        }\n        data.addLoadedSearchText(searchText);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/MoveEntryTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\n/**\n * Moves an entry from one calendar to another.\n *\n * Created by gdiaz on 19/03/2017.\n */\npublic final class MoveEntryTask extends GoogleTask<GoogleEntry> {\n\n    private final GoogleEntry entry;\n    private final GoogleCalendar from;\n    private final GoogleCalendar to;\n    private final GoogleAccount account;\n\n    public MoveEntryTask(GoogleEntry entry, GoogleCalendar from, GoogleCalendar to, GoogleAccount account) {\n        this.entry = entry;\n        this.from = from;\n        this.to = to;\n        this.account = account;\n        this.logItem.setCalendar(from.getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.MOVE;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Moving \" + entry + \" to \" + to;\n    }\n\n    @Override\n    protected GoogleEntry call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).moveEntry(entry, from, to);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/RefreshCalendarsTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.data.GoogleCalendarData;\nimport com.calendarfx.google.view.data.IGoogleCalendarDataProvider;\nimport com.calendarfx.google.view.log.ActionType;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * Task that allows to refresh the data of a single calendar.\n *\n * Created by gdiaz on 10/04/2017.\n */\npublic final class RefreshCalendarsTask extends GoogleTask<List<GoogleCalendar>> {\n\n    private final GoogleAccount account;\n    private final IGoogleCalendarDataProvider provider;\n\n    public RefreshCalendarsTask(GoogleAccount account, IGoogleCalendarDataProvider provider) {\n        this.account = account;\n        this.provider = provider;\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.REFRESH;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Refreshing all calendars\";\n    }\n\n    @Override\n    protected List<GoogleCalendar> call() throws Exception {\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).getCalendars();\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n\n        List<GoogleCalendar> oldCalendars = account.getGoogleCalendars();\n        List<GoogleCalendar> newCalendars = getValue();\n        List<GoogleCalendar> updCalendars = new ArrayList<>();\n\n        for (Iterator<GoogleCalendar> newIterator = newCalendars.iterator(); newIterator.hasNext(); ) {\n            GoogleCalendar newCalendar = newIterator.next();\n\n            for (Iterator<GoogleCalendar> oldIterator = oldCalendars.iterator(); oldIterator.hasNext(); ) {\n                GoogleCalendar oldCalendar = oldIterator.next();\n                if (newCalendar.equals(oldCalendar)) {\n                    oldIterator.remove();\n                    newIterator.remove();\n                    updCalendars.add(oldCalendar);\n                    GoogleCalendarData data = provider.getCalendarData(oldCalendar);\n                    if (data != null) {\n                        data.clear();\n                    }\n                    break;\n                }\n            }\n        }\n\n        updCalendars.forEach(GoogleCalendar::clear);\n        account.getCalendars().removeAll(oldCalendars);\n        account.getCalendars().addAll(newCalendars);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/task/UpdateEntryTask.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.task;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.view.log.ActionType;\n\nimport java.util.Map;\n\n/**\n * Task that updates one entry in google.\n *\n * Created by gdiaz on 12/03/2017.\n */\npublic final class UpdateEntryTask extends GoogleTask<GoogleEntry> {\n\n    private static final long TWO_SECONDS = 2 * 1000;\n\n    private GoogleEntry entry;\n    private final GoogleAccount account;\n    private final Map<GoogleEntry, UpdateEntryTask> updateTasks;\n\n    public UpdateEntryTask(GoogleEntry entry, GoogleAccount account, Map<GoogleEntry, UpdateEntryTask> updateTasks) {\n        this.entry = entry;\n        this.account = account;\n        this.updateTasks = updateTasks;\n        this.logItem.setCalendar(entry.getCalendar().getName());\n        this.logItem.setDescription(getDescription());\n    }\n\n    public void append(GoogleEntry newVersion) {\n        assert (entry.equals(newVersion));\n        this.entry = newVersion;\n        this.logItem.setDescription(getDescription());\n    }\n\n    @Override\n    public ActionType getAction() {\n        return ActionType.UPDATE;\n    }\n\n    @Override\n    public String getDescription() {\n        return \"Update \" + entry;\n    }\n\n    @Override\n    protected GoogleEntry call() throws Exception {\n        Thread.sleep(TWO_SECONDS);\n        return GoogleConnector.getInstance().getCalendarService(account.getId()).updateEntry(entry);\n    }\n\n    @Override\n    protected void succeeded() {\n        super.succeeded();\n        updateTasks.remove(entry);\n    }\n\n    @Override\n    protected void cancelled() {\n        super.cancelled();\n        updateTasks.remove(entry);\n    }\n\n    @Override\n    protected void failed() {\n        super.failed();\n        updateTasks.remove(entry);\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/thread/CalendarViewTimeUpdateThread.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.thread;\n\nimport com.calendarfx.view.CalendarView;\nimport javafx.application.Platform;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\n/**\n * Thread that updates the current tine on the calendar view\n *\n * Created by gdiaz on 4/05/2017.\n */\npublic class CalendarViewTimeUpdateThread extends Thread {\n\n    private static final int TEN_SECONDS = 10000;\n\n    private final CalendarView calendarView;\n\n    public CalendarViewTimeUpdateThread(CalendarView calendarView) {\n        super(\"Google-Calendar-Update Current Time\");\n        this.calendarView = calendarView;\n        setPriority(MIN_PRIORITY);\n        setDaemon(true);\n    }\n\n    @Override\n    @SuppressWarnings(\"InfiniteLoopStatement\")\n    public void run() {\n        while (true) {\n            Platform.runLater(() -> {\n                calendarView.setToday(LocalDate.now());\n                calendarView.setTime(LocalTime.now());\n            });\n\n            try {\n                sleep(TEN_SECONDS);\n            } catch (InterruptedException e) {\n                // Do nothing\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/thread/GoogleAutoRefreshThread.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.thread;\n\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.google.view.data.IGoogleCalendarDataProvider;\nimport com.calendarfx.google.view.task.RefreshCalendarsTask;\nimport com.google.common.base.Preconditions;\n\n/**\n * Thread that performs the automatic refreshing.\n *\n * Created by gdiaz on 5/05/2017.\n */\npublic class GoogleAutoRefreshThread extends Thread {\n\n    private final Object LOCK = new Object();\n\n    private final IGoogleCalendarDataProvider provider;\n    private RefreshCalendarsTask task;\n    private long delay;\n\n    public GoogleAutoRefreshThread(IGoogleCalendarDataProvider provider) {\n        this.provider = provider;\n        this.delay = RefreshInterval.EVERY_5_MINUTES.getTime();\n        setName(\"Google-Calendar-Auto refresh Thread\");\n        setPriority(NORM_PRIORITY);\n        setDaemon(true);\n    }\n\n    public long getDelay() {\n        return delay;\n    }\n\n    public void setDelay(long delay) {\n        Preconditions.checkArgument(delay >= 0);\n        synchronized (LOCK) {\n            this.delay = delay;\n        }\n    }\n\n    public void restart() {\n        synchronized (LOCK) {\n            if (task != null) {\n                task.cancel();\n                task = null;\n            }\n        }\n        interrupt();\n    }\n\n    @Override\n    @SuppressWarnings(\"InfiniteLoopStatement\")\n    public void run() {\n        while (true) {\n            try {\n                long wait;\n\n                synchronized (LOCK) {\n                    wait = delay;\n                }\n\n                if (wait > 0) {\n                    sleep(wait);\n                }\n            } catch (InterruptedException e) {\n                // Do nothing\n            }\n\n            synchronized (LOCK) {\n                if (delay > 0 && SecurityService.getInstance().isLoggedIn()) {\n                    task = new RefreshCalendarsTask(SecurityService.getInstance().getLoggedAccount(), provider);\n                    GoogleTaskExecutor.getInstance().execute(task);\n                }\n            }\n        }\n    }\n\n    /**\n     * Enum used to determine the internals of refreshing.\n     */\n    public enum RefreshInterval {\n\n        NEVER(0, \"Never\"),\n\n        EVERY_MINUTE(1000 * 60, \"Every Minute\"),\n\n        EVERY_2_MINUTES(1000 * 60 * 2, \"Every 2 Minutes\"),\n\n        EVERY_5_MINUTES(1000 * 60 * 5, \"Every 5 Minutes\"),\n\n        EVERY_10_MINUTES(1000 * 60 * 10, \"Every 10 Minutes\"),\n\n        EVERY_30_MINUTES(1000 * 60 * 30, \"Every 30 Minutes\");\n\n        private final long time;\n        private final String name;\n\n        RefreshInterval(long time, String name) {\n            this.time = time;\n            this.name = name;\n        }\n\n        public long getTime() {\n            return time;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/thread/GoogleNotificationPopupThread.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.thread;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.model.GoogleEntryReminder;\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarView;\nimport com.google.common.collect.Lists;\nimport javafx.application.Platform;\nimport org.controlsfx.control.Notifications;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Thread that shows notifications of entries that are about to happen.\n *\n * Created by gdiaz on 6/05/2017.\n */\npublic class GoogleNotificationPopupThread extends Thread {\n\n    private static final long ONE_MINUTE = 1000 * 30;\n\n    private final CalendarView calendarView;\n\n    public GoogleNotificationPopupThread(CalendarView calendarView) {\n        this.calendarView = calendarView;\n        setDaemon(true);\n        setName(\"Google-Notification-Thread\");\n        setPriority(NORM_PRIORITY);\n    }\n\n    @SuppressWarnings(\"InfiniteLoopStatement\")\n    @Override\n    public void run() {\n        while (true) {\n            try {\n                sleep(ONE_MINUTE);\n            } catch (InterruptedException e) {\n                // Do nothing\n            }\n\n            if (SecurityService.getInstance().isLoggedIn()) {\n                GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n                LocalDate today = calendarView.getToday();\n                LocalDateTime now = calendarView.getToday().atTime(LocalTime.now());\n\n                for (GoogleCalendar calendar : account.getGoogleCalendars()) {\n                    Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(today, today, calendarView.getZoneId());\n                    for (List<Entry<?>> list : entries.values()) {\n                        for (Entry<?> e : list) {\n                            if (e instanceof GoogleEntry) {\n                                GoogleEntry entry = (GoogleEntry) e;\n                                if (isSubjectOfNotification(entry, now)) {\n                                    showNotification(entry);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean isSubjectOfNotification(GoogleEntry entry, LocalDateTime now) {\n        List<GoogleEntryReminder> reminders = Lists.newArrayList();\n\n        reminders.addAll(entry.getReminders());\n        if (reminders.isEmpty()) {\n            reminders.addAll(((GoogleCalendar) entry.getCalendar()).getDefaultReminders());\n        }\n\n        for (GoogleEntryReminder reminder : reminders) {\n            if (reminder.getMethod() != GoogleEntryReminder.RemindMethod.POPUP) {\n                continue;\n            }\n\n            if (reminder.getMinutes() == null || reminder.getMinutes() < 0) {\n                continue;\n            }\n\n            if (!now.isBefore(entry.getStartAsLocalDateTime())) {\n                continue;\n            }\n\n            long distanceMinutes = now.until(entry.getStartAsLocalDateTime(), ChronoUnit.MINUTES);\n            if (distanceMinutes == reminder.getMinutes()) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    private void showNotification(GoogleEntry entry) {\n        Platform.runLater(() -> {\n            GoogleCalendar calendar = (GoogleCalendar) entry.getCalendar();\n            Notifications.create().title(calendar.getName()).text(entry.getTitle()).showInformation();\n        });\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/com/calendarfx/google/view/thread/GoogleTaskExecutor.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.thread;\n\nimport com.calendarfx.google.view.log.LogItem;\nimport com.calendarfx.google.view.task.GoogleTask;\nimport com.google.common.util.concurrent.ThreadFactoryBuilder;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ReadOnlyDoubleProperty;\nimport javafx.beans.property.ReadOnlyDoubleWrapper;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.concurrent.WorkerStateEvent;\nimport javafx.event.EventHandler;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Executor class that allows to have some background tasks executed.\n *\n * Created by gdiaz on 22/02/2017.\n */\npublic final class GoogleTaskExecutor {\n\n    private static GoogleTaskExecutor instance;\n\n    public static GoogleTaskExecutor getInstance() {\n        if (instance == null) {\n            instance = new GoogleTaskExecutor();\n        }\n        return instance;\n    }\n\n    private final IntegerProperty pendingTasks = new SimpleIntegerProperty(this, \"pendingTasks\", 0);\n\n    private final ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactoryBuilder()\n            .setDaemon(true)\n            .setNameFormat(\"Google-Calendar-Executor-%d\")\n            .setPriority(Thread.MIN_PRIORITY)\n            .build());\n\n    private GoogleTaskExecutor() {\n        super();\n        progress.bind(Bindings.divide(\n                Bindings.when(pendingTasks.isEqualTo(0)).then(0).otherwise(1),\n                Bindings.when(pendingTasks.isEqualTo(0)).then(Double.MAX_VALUE).otherwise(pendingTasks))\n        );\n    }\n\n    private final ObservableList<LogItem> log = FXCollections.observableArrayList();\n\n    public ObservableList<LogItem> getLog() {\n        return log;\n    }\n\n    public void clearLog() {\n        log.clear();\n    }\n\n    private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, \"progress\", 0);\n\n    public ReadOnlyDoubleProperty progressProperty() {\n        return progress.getReadOnlyProperty();\n    }\n\n    public void execute(GoogleTask<?> task) {\n        updatePendingTasks(task);\n        executor.submit(task);\n    }\n\n    public void executeImmediate(GoogleTask<?> task) {\n        updatePendingTasks(task);\n        task.run();\n    }\n\n    private void updatePendingTasks(GoogleTask<?> task) {\n        Util.runInFXThread(() -> {\n            pendingTasks.set(pendingTasks.get() + 1);\n            EventHandler<WorkerStateEvent> handler = evt -> pendingTasks.set(pendingTasks.get() - 1);\n            task.setOnFailed(handler);\n            task.setOnSucceeded(handler);\n            log.add(0, task.getLogItem());\n        });\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/GoogleCalendarAppViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.GoogleConnector;\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.google.view.GoogleCalendarAppView;\nimport com.calendarfx.google.view.popover.GoogleEntryPopOverContentPane;\nimport com.calendarfx.google.view.task.InsertCalendarTask;\nimport com.calendarfx.google.view.task.InsertEntryTask;\nimport com.calendarfx.google.view.task.LoadAllCalendarsTask;\nimport com.calendarfx.google.view.thread.CalendarViewTimeUpdateThread;\nimport com.calendarfx.google.view.thread.GoogleAutoRefreshThread;\nimport com.calendarfx.google.view.thread.GoogleNotificationPopupThread;\nimport com.calendarfx.google.view.thread.GoogleTaskExecutor;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.LoadEvent;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DeveloperConsole;\nimport com.calendarfx.view.VirtualGrid;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.scene.Node;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.input.KeyCombination;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.web.WebView;\nimport javafx.stage.Window;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PopOver;\nimport org.controlsfx.control.StatusBar;\n\nimport java.net.CookieHandler;\nimport java.net.CookieManager;\nimport java.time.DayOfWeek;\nimport java.time.Duration;\nimport java.time.ZonedDateTime;\nimport java.util.function.Consumer;\n\n/**\n * Skin class for the {@link GoogleCalendarAppView} control, which allows to display\n * the Google Login pane.\n *\n * @author Gabriel Diaz, 14.02.2015.\n */\npublic class GoogleCalendarAppViewSkin extends SkinBase<GoogleCalendarAppView> {\n\n    private final WebView loginView;\n    private final BorderPane calendarPane;\n    private final CalendarView calendarView;\n\n    private final CookieManager cookieManager;\n    private final GoogleCalendarDataManager dataManager;\n    private final GoogleSyncManager syncManager;\n    private final GoogleCalendarSearchTextManager searchProvider;\n\n    public GoogleCalendarAppViewSkin(GoogleCalendarAppView control) {\n        super(control);\n\n        calendarView = control.getCalendarView();\n        dataManager = new GoogleCalendarDataManager();\n        cookieManager = new CookieManager();\n        syncManager = new GoogleSyncManager();\n        searchProvider = new GoogleCalendarSearchTextManager(dataManager);\n\n        CalendarViewTimeUpdateThread timeUpdateThread = new CalendarViewTimeUpdateThread(calendarView);\n        GoogleAutoRefreshThread autoRefreshThread = new GoogleAutoRefreshThread(dataManager);\n        GoogleNotificationPopupThread notificationThread = new GoogleNotificationPopupThread(calendarView);\n\n        loginView = new WebView();\n        loginView.setVisible(false);\n        loginView.getEngine().titleProperty().addListener((obs, oldValue, newValue) -> {\n            if (newValue != null && newValue.contains(\"Success code=\")) {\n                String code = newValue.split(\"code=\")[1];\n                if (SecurityService.getInstance().authorize(code)) {\n                    login();\n                }\n            }\n        });\n\n        Bindings.bindContentBidirectional(control.getLogPane().getItems(), GoogleTaskExecutor.getInstance().getLog());\n\n        StatusBar statusBar = new StatusBar();\n        statusBar.textProperty().bind(Bindings.when(GoogleTaskExecutor.getInstance().progressProperty().isEqualTo(0)).then(\"\").otherwise(\"Loading...\"));\n        statusBar.progressProperty().bind(GoogleTaskExecutor.getInstance().progressProperty());\n\n        calendarView.addEventFilter(LoadEvent.LOAD, dataManager);\n        calendarView.setEntryFactory(new GoogleEntryCreateCallback());\n        calendarView.setCalendarSourceFactory(new GoogleCalendarCreateCallback(control.getScene().getWindow()));\n        calendarView.setEntryDetailsPopOverContentCallback(new GoogleEntryPopOverContentProvider());\n\n        calendarPane = new BorderPane();\n        calendarPane.setTop(createMenuBar(autoRefreshThread));\n        calendarPane.setCenter(calendarView);\n        calendarPane.setBottom(statusBar);\n\n        DeveloperConsole developerConsole = calendarView.getDeveloperConsole();\n        if (developerConsole != null) {\n            developerConsole.getTabPane().getTabs().add(new Tab(\"Google\", control.getLogPane()));\n        }\n\n        getChildren().add(new StackPane(loginView, calendarPane));\n        CookieHandler.setDefault(cookieManager);\n\n        timeUpdateThread.start();\n        autoRefreshThread.start();\n        notificationThread.start();\n\n        attemptAutoLogin();\n    }\n\n    private MenuBar createMenuBar(GoogleAutoRefreshThread autoRefreshThread) {\n        MenuItem logoutItem = new MenuItem(\"Logout\");\n        logoutItem.setOnAction(evt -> logout());\n\n        MenuItem exitItem = new MenuItem(\"Exit\");\n        exitItem.setAccelerator(KeyCombination.keyCombination(\"shortcut+q\"));\n        exitItem.setOnAction(evt -> Platform.exit());\n\n        Menu fileMenu = new Menu(\"File\");\n        fileMenu.getItems().add(logoutItem);\n        fileMenu.getItems().add(exitItem);\n\n        MenuItem refreshItem = new MenuItem(\"Refresh\");\n        refreshItem.setOnAction(evt -> autoRefreshThread.restart());\n        refreshItem.setAccelerator(KeyCombination.keyCombination(\"F5\"));\n\n        ToggleGroup intervalGroup = new ToggleGroup();\n        Menu autoRefreshItem = new Menu(\"Auto Refresh\");\n\n        for (GoogleAutoRefreshThread.RefreshInterval interval : GoogleAutoRefreshThread.RefreshInterval.values()) {\n            RadioMenuItem intervalItem = new RadioMenuItem(interval.getName());\n            intervalItem.setOnAction(evt -> autoRefreshThread.setDelay(interval.getTime()));\n            intervalItem.setToggleGroup(intervalGroup);\n            intervalItem.setSelected(interval.getTime() == autoRefreshThread.getDelay());\n            autoRefreshItem.getItems().add(intervalItem);\n        }\n\n        Menu viewMenu = new Menu(\"View\");\n        viewMenu.getItems().addAll(refreshItem, autoRefreshItem);\n\n        MenuBar menuBar = new MenuBar();\n        menuBar.setUseSystemMenuBar(true);\n        menuBar.getMenus().add(fileMenu);\n        menuBar.getMenus().add(viewMenu);\n\n        return menuBar;\n    }\n\n    private void attemptAutoLogin() {\n        if (SecurityService.getInstance().isAuthorized()) {\n            login();\n        } else {\n            showLoginView();\n        }\n    }\n\n    private void login() {\n        GoogleAccount account = SecurityService.getInstance().login();\n        if (account != null) {\n            calendarView.getCalendarSources().setAll(account);\n            account.addCalendarListeners(dataManager, searchProvider, syncManager);\n            GoogleTaskExecutor.getInstance().execute(new LoadAllCalendarsTask(account));\n            showCalendarPane();\n        } else {\n            showLoginView();\n        }\n    }\n\n    private void logout() {\n        if (SecurityService.getInstance().isLoggedIn()) {\n            GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n            calendarView.getCalendarSources().clear();\n            dataManager.clearData();\n            account.removeCalendarListeners(dataManager, searchProvider, syncManager);\n            GoogleTaskExecutor.getInstance().clearLog();\n            SecurityService.getInstance().logout();\n        }\n        showLoginView();\n    }\n\n    private void showLoginView() {\n        cookieManager.getCookieStore().removeAll();\n        loginView.getEngine().load(GoogleConnector.getInstance().getAuthorizationURL());\n        loginView.setVisible(true);\n        calendarPane.setVisible(false);\n    }\n\n    private void showCalendarPane() {\n        cookieManager.getCookieStore().removeAll();\n        loginView.getEngine().load(GoogleConnector.getInstance().getAuthorizationURL());\n        loginView.setVisible(false);\n        calendarPane.setVisible(true);\n    }\n\n    /**\n     * Factory for calendars.\n     *\n     * Created by gdiaz on 5/05/2017.\n     */\n    private static class GoogleCalendarCreateCallback implements\n            Callback<DateControl.CreateCalendarSourceParameter, CalendarSource>,\n            Consumer<GoogleCalendarCreateView.CalendarViewBean> {\n\n        private final Window owner;\n\n        GoogleCalendarCreateCallback(Window owner) {\n            this.owner = owner;\n        }\n\n        @Override\n        public CalendarSource call(DateControl.CreateCalendarSourceParameter param) {\n            if (SecurityService.getInstance().isLoggedIn()) {\n                GoogleCalendarCreateView view = new GoogleCalendarCreateView(this);\n                view.show(owner);\n            }\n            return null;\n        }\n\n        @Override\n        public void accept(GoogleCalendarCreateView.CalendarViewBean bean) {\n            GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n            GoogleCalendar calendar = account.createCalendar(bean.getName(), bean.getStyle());\n            GoogleTaskExecutor.getInstance().execute(new InsertCalendarTask(calendar, account));\n        }\n    }\n\n    /**\n     * Provider of the google entry pop over content.\n     *\n     * Created by gdiaz on 5/05/2017.\n     */\n    private static class GoogleEntryPopOverContentProvider implements Callback<DateControl.EntryDetailsPopOverContentParameter, Node> {\n        private PopOver popOver;\n        private GoogleEntry entry;\n\n        private final InvalidationListener detachListener = obs -> {\n            if (getEntry().isFullDay() && !getPopOver().isDetached()) {\n                getPopOver().setDetached(true);\n            }\n        };\n\n        private final WeakInvalidationListener weakListener = new WeakInvalidationListener(detachListener);\n\n        @Override\n        public Node call(DateControl.EntryDetailsPopOverContentParameter param) {\n            popOver = param.getPopOver();\n            entry = (GoogleEntry) param.getEntry();\n\n            entry.fullDayProperty().addListener(weakListener);\n            popOver.setOnHidden(evt -> entry.fullDayProperty().removeListener(weakListener));\n\n            return new GoogleEntryPopOverContentPane(entry, param.getDateControl().getCalendars(), param.getDateControl());\n        }\n\n        public PopOver getPopOver() {\n            return popOver;\n        }\n\n        public GoogleEntry getEntry() {\n            return entry;\n        }\n    }\n\n    /**\n     * Factory for google entries.\n     *\n     * Created by gdiaz on 5/05/2017.\n     */\n    private static class GoogleEntryCreateCallback implements Callback<DateControl.CreateEntryParameter, Entry<?>> {\n\n        @Override\n        public Entry<?> call(DateControl.CreateEntryParameter param) {\n            if (SecurityService.getInstance().isLoggedIn()) {\n                GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n                GoogleCalendar primaryCalendar = account.getPrimaryCalendar();\n\n                if (primaryCalendar != null) {\n                    DateControl control = param.getDateControl();\n                    VirtualGrid grid = control.getVirtualGrid();\n                    ZonedDateTime start = param.getZonedDateTime();\n                    DayOfWeek firstDayOfWeek = control.getFirstDayOfWeek();\n                    ZonedDateTime lowerTime = grid.adjustTime(start, false, firstDayOfWeek);\n                    ZonedDateTime upperTime = grid.adjustTime(start, true, firstDayOfWeek);\n\n                    if (Duration.between(start, lowerTime).abs().minus(Duration.between(start, upperTime).abs()).isNegative()) {\n                        start = lowerTime;\n                    }\n\n                    GoogleEntry entry = primaryCalendar.createEntry(start, control instanceof AllDayView);\n                    GoogleTaskExecutor.getInstance().execute(new InsertEntryTask(entry, primaryCalendar, account));\n                }\n            }\n\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/GoogleCalendarCreateView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.CalendarView;\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.Window;\n\nimport java.util.function.Consumer;\n\n/**\n * Pane that allows to enter a web URL of an iCal.\n *\n * Created by gdiaz on 5/01/2017.\n */\nfinal class GoogleCalendarCreateView extends BorderPane {\n\n    private final TextField nameField;\n\n    private final ComboBox<Calendar.Style> styleComboBox;\n\n    private Stage dialog;\n\n    GoogleCalendarCreateView(Consumer<CalendarViewBean> onAccept) {\n        nameField = new TextField();\n        styleComboBox = new ComboBox<>();\n        styleComboBox.getItems().setAll(Calendar.Style.values());\n        styleComboBox.setButtonCell(new StyleCell());\n        styleComboBox.setCellFactory(listView -> new StyleCell());\n\n        Button acceptButton = new Button(\"Accept\");\n        acceptButton.disableProperty().bind(Bindings.or(Bindings.isEmpty(nameField.textProperty()), Bindings.isNull(styleComboBox.valueProperty())));\n        acceptButton.setOnAction(evt -> {\n            if (onAccept != null) {\n                CalendarViewBean bean = new CalendarViewBean();\n                bean.setName(nameField.getText());\n                bean.setStyle(styleComboBox.getValue());\n                onAccept.accept(bean);\n            }\n            close();\n        });\n        Button cancelButton = new Button(\"Cancel\");\n        cancelButton.setOnAction(evt -> close());\n\n        GridPane gridPane = new GridPane();\n        gridPane.add(new Label(\"Name\"), 0, 0);\n        gridPane.add(nameField, 1, 0);\n        gridPane.add(new Label(\"Color\"), 0, 1);\n        gridPane.add(styleComboBox, 1, 1);\n        gridPane.getStyleClass().add(\"center\");\n        gridPane.setVgap(5);\n        gridPane.setHgap(5);\n        gridPane.setPadding(new Insets(10));\n\n        GridPane.setHgrow(nameField, Priority.ALWAYS);\n        GridPane.setHgrow(styleComboBox, Priority.ALWAYS);\n\n        ButtonBar buttonBar = new ButtonBar();\n        buttonBar.getButtons().addAll(acceptButton, cancelButton);\n\n        VBox bottomPane = new VBox();\n        bottomPane.getChildren().addAll(new Separator(), buttonBar);\n        bottomPane.getStyleClass().add(\"bottom\");\n        bottomPane.setFillWidth(true);\n        bottomPane.setSpacing(10);\n\n        setCenter(gridPane);\n        setBottom(bottomPane);\n        setPadding(new Insets(15));\n        setPrefWidth(300);\n        getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n    }\n\n    void show(Window owner) {\n        if (dialog == null) {\n            dialog = new Stage();\n            dialog.initOwner(owner);\n            dialog.setScene(new Scene(this));\n            dialog.sizeToScene();\n            dialog.centerOnScreen();\n            dialog.setTitle(\"Add Calendar\");\n            dialog.initModality(Modality.APPLICATION_MODAL);\n        }\n        dialog.showAndWait();\n    }\n\n    private void close() {\n        nameField.setText(null);\n        styleComboBox.setValue(null);\n        if (dialog != null) {\n            dialog.hide();\n        }\n    }\n\n    private static class StyleCell extends ListCell<Calendar.Style> {\n        @Override\n        protected void updateItem(Calendar.Style item, boolean empty) {\n            super.updateItem(item, empty);\n            if (item != null && !empty) {\n                Rectangle icon = new Rectangle(12, 12);\n                icon.getStyleClass().add(item.name().toLowerCase() + \"-icon\");\n                setGraphic(icon);\n                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n            }\n        }\n    }\n\n    static class CalendarViewBean {\n\n        private String name;\n        private Calendar.Style style;\n\n        Calendar.Style getStyle() {\n            return style;\n        }\n\n        void setStyle(Calendar.Style style) {\n            this.style = style;\n        }\n\n        String getName() {\n            return name;\n        }\n\n        void setName(String name) {\n            this.name = name;\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/GoogleCalendarDataManager.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.google.view.data.GoogleCalendarData;\nimport com.calendarfx.google.view.data.IGoogleCalendarDataProvider;\nimport com.calendarfx.google.view.data.Slice;\nimport com.calendarfx.google.view.task.LoadEntriesBySliceTask;\nimport com.calendarfx.google.view.thread.GoogleTaskExecutor;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.LoadEvent;\nimport javafx.collections.ListChangeListener;\nimport javafx.event.EventHandler;\n\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Loader class for calendars and entries.\n *\n * Created by gdiaz on 27/02/2017.\n */\nfinal class GoogleCalendarDataManager implements IGoogleCalendarDataProvider, ListChangeListener<Calendar>, EventHandler<LoadEvent> {\n\n    private final Map<GoogleCalendar, GoogleCalendarData> calendarsData = new HashMap<>();\n\n    GoogleCalendarDataManager() {\n        super();\n    }\n\n    @Override\n    public GoogleCalendarData getCalendarData(GoogleCalendar calendar, boolean create) {\n        GoogleCalendarData data = calendarsData.get(calendar);\n        if (data == null && create) {\n            data = new GoogleCalendarData();\n            calendarsData.put(calendar, data);\n        }\n        return data;\n    }\n\n    @Override\n    public void removeCalendarData(GoogleCalendar calendar) {\n        calendarsData.remove(calendar);\n    }\n\n    @Override\n    public void clearData() {\n        calendarsData.clear();\n    }\n\n    @Override\n    public void onChanged(Change<? extends Calendar> c) {\n        List<GoogleCalendar> removed = new ArrayList<>();\n\n        while (c.next()) {\n            for (Calendar calendar : c.getRemoved()) {\n                if (calendar instanceof GoogleCalendar) {\n                    removed.add((GoogleCalendar) calendar);\n                }\n            }\n\n            for (Calendar calendar : c.getAddedSubList()) {\n                if (calendar instanceof GoogleCalendar) {\n                    removed.remove(calendar);\n                }\n            }\n        }\n\n        for (GoogleCalendar calendar : removed) {\n            removeCalendarData(calendar);\n        }\n    }\n\n    @Override\n    public void handle(LoadEvent evt) {\n        if (SecurityService.getInstance().isLoggedIn()) {\n            List<Slice> slices = Slice.split(evt.getStartDate(), evt.getEndDate());\n\n            if (!slices.isEmpty()) {\n                ZoneId zoneId = evt.getZoneId();\n                GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n\n                for (GoogleCalendar cal : account.getGoogleCalendars()) {\n                    GoogleCalendarData data = getCalendarData(cal, true);\n                    List<Slice> unloaded = data.getUnloadedSlices(slices);\n                    if (!unloaded.isEmpty()) {\n                        data.addInProgressSlices(unloaded);\n                        for (Slice us : unloaded) {\n                            LoadEntriesBySliceTask task = new LoadEntriesBySliceTask(account, cal, data, us, zoneId);\n                            GoogleTaskExecutor.getInstance().execute(task);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/GoogleCalendarSearchTextManager.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.IGoogleCalendarSearchTextProvider;\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.google.view.data.GoogleCalendarData;\nimport com.calendarfx.google.view.data.IGoogleCalendarDataProvider;\nimport com.calendarfx.google.view.task.LoadEntriesByTextTask;\nimport com.calendarfx.google.view.thread.GoogleTaskExecutor;\nimport com.calendarfx.model.Calendar;\nimport javafx.collections.ListChangeListener;\n\n/**\n * Default implementation of the provider that searches entries in google synchronously.\n *\n * Created by gdiaz on 5/05/2017.\n */\nfinal class GoogleCalendarSearchTextManager implements IGoogleCalendarSearchTextProvider, ListChangeListener<Calendar> {\n\n    private final IGoogleCalendarDataProvider provider;\n\n    GoogleCalendarSearchTextManager(IGoogleCalendarDataProvider provider) {\n        this.provider = provider;\n    }\n\n    @Override\n    public void search(GoogleCalendar calendar, String text) {\n        if (SecurityService.getInstance().isLoggedIn()) {\n            GoogleCalendarData data = provider.getCalendarData(calendar, true);\n            if (!data.isLoadedSearchText(text)) {\n                GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n                LoadEntriesByTextTask task = new LoadEntriesByTextTask(text, calendar, data, account);\n                GoogleTaskExecutor.getInstance().executeImmediate(task);\n            }\n        }\n    }\n\n    @Override\n    public void onChanged(Change<? extends Calendar> c) {\n        while (c.next()) {\n            for (Calendar calendar : c.getRemoved()) {\n                if (calendar instanceof GoogleCalendar) {\n                    ((GoogleCalendar) calendar).setSearchTextProvider(null);\n                }\n            }\n\n            for (Calendar calendar : c.getAddedSubList()) {\n                if (calendar instanceof GoogleCalendar) {\n                    ((GoogleCalendar) calendar).setSearchTextProvider(this);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/GoogleSyncManager.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view;\n\nimport com.calendarfx.google.model.GoogleAccount;\nimport com.calendarfx.google.model.GoogleCalendar;\nimport com.calendarfx.google.model.GoogleCalendarEvent;\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.google.service.SecurityService;\nimport com.calendarfx.google.view.task.DeleteEntryTask;\nimport com.calendarfx.google.view.task.InsertEntryTask;\nimport com.calendarfx.google.view.task.MoveEntryTask;\nimport com.calendarfx.google.view.task.UpdateEntryTask;\nimport com.calendarfx.google.view.thread.GoogleTaskExecutor;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.Entry;\nimport javafx.collections.ListChangeListener;\nimport javafx.event.EventHandler;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Class in charge of receiving calendar events and synchronize data between the app and google.\n *\n * Created by gdiaz on 06/03/2017.\n */\nfinal class GoogleSyncManager implements EventHandler<CalendarEvent>, ListChangeListener<Calendar> {\n\n    private final Map<GoogleEntry, UpdateEntryTask> updateTasks = new HashMap<>();\n\n    @Override\n    public void onChanged(Change<? extends Calendar> c) {\n        while (c.next()) {\n            for (Calendar calendar : c.getRemoved()) {\n                calendar.removeEventHandler(this);\n            }\n\n            for (Calendar calendar : c.getAddedSubList()) {\n                calendar.addEventHandler(this);\n            }\n        }\n    }\n\n    @Override\n    public void handle(CalendarEvent evt) {\n        if (SecurityService.getInstance().isLoggedIn()) {\n            GoogleAccount account = SecurityService.getInstance().getLoggedAccount();\n\n            if (requiresInsertEntry(evt)) {\n                insertEntry(evt, account);\n            } else if (requiresDeleteEntry(evt)) {\n                deleteEntry(evt, account);\n            } else if (requiresUpdateEntry(evt)) {\n                updateEntry(evt, account);\n            } else if (requiresMoveEntry(evt)) {\n                moveEntry(evt, account);\n            }\n        }\n    }\n\n    private void insertEntry(CalendarEvent evt, GoogleAccount account) {\n        GoogleEntry entry = (GoogleEntry) evt.getEntry();\n        GoogleCalendar calendar = (GoogleCalendar) evt.getCalendar();\n        GoogleTaskExecutor.getInstance().execute(new InsertEntryTask(entry, calendar, account));\n    }\n\n    private void updateEntry(CalendarEvent evt, GoogleAccount account) {\n        GoogleEntry entry = (GoogleEntry) evt.getEntry();\n        UpdateEntryTask updateTask = updateTasks.get(entry);\n        if (updateTask == null) {\n            updateTask = new UpdateEntryTask(entry, account, updateTasks);\n            updateTasks.put(entry, updateTask);\n            GoogleTaskExecutor.getInstance().execute(updateTask);\n        } else {\n            updateTask.append(entry);\n        }\n    }\n\n    private void deleteEntry(CalendarEvent evt, GoogleAccount account) {\n        GoogleEntry entry = (GoogleEntry) evt.getEntry();\n        GoogleCalendar calendar = (GoogleCalendar) evt.getOldCalendar();\n        GoogleTaskExecutor.getInstance().execute(new DeleteEntryTask(entry, calendar, account));\n    }\n\n    private void moveEntry(CalendarEvent evt, GoogleAccount account) {\n        GoogleEntry entry = (GoogleEntry) evt.getEntry();\n        GoogleCalendar from = (GoogleCalendar) evt.getOldCalendar();\n        GoogleCalendar to = (GoogleCalendar) evt.getCalendar();\n        GoogleTaskExecutor.getInstance().execute(new MoveEntryTask(entry, from, to, account));\n    }\n\n    private boolean requiresInsertEntry(CalendarEvent evt) {\n        if (evt != null && evt.getEventType().equals(GoogleCalendarEvent.ENTRY_CALENDAR_CHANGED) && evt.getCalendar() != null && evt.getOldCalendar() == null) {\n            Entry<?> entry = evt.getEntry();\n            if (entry instanceof GoogleEntry && !entry.isRecurrence()) {\n                return !((GoogleEntry) entry).existsInGoogle();\n            }\n        }\n        return false;\n    }\n\n    private boolean requiresDeleteEntry(CalendarEvent evt) {\n        if (evt != null && evt.getEventType().equals(GoogleCalendarEvent.ENTRY_CALENDAR_CHANGED) && evt.getCalendar() == null && evt.getOldCalendar() != null) {\n            Entry<?> entry = evt.getEntry();\n            if (entry instanceof GoogleEntry && !entry.isRecurrence()) {\n                return ((GoogleEntry) entry).existsInGoogle();\n            }\n        }\n        return false;\n    }\n\n    private boolean requiresUpdateEntry(CalendarEvent evt) {\n        if (evt != null && !evt.getEventType().equals(GoogleCalendarEvent.ENTRY_CALENDAR_CHANGED) && evt.getEventType().getSuperType().equals(GoogleCalendarEvent.ENTRY_CHANGED)) {\n            Entry<?> entry = evt.getEntry();\n            if (entry instanceof GoogleEntry && !entry.isRecurrence()) {\n                return ((GoogleEntry) entry).existsInGoogle();\n            }\n        }\n        return false;\n    }\n\n    private boolean requiresMoveEntry(CalendarEvent evt) {\n        if (evt != null && evt.getEventType().equals(GoogleCalendarEvent.ENTRY_CALENDAR_CHANGED) && evt.getCalendar() != null && evt.getOldCalendar() != null) {\n            Entry<?> entry = evt.getEntry();\n            if (entry instanceof GoogleEntry && !entry.isRecurrence()) {\n                return ((GoogleEntry) entry).existsInGoogle();\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/impl/com/calendarfx/google/view/log/LogPaneSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.google.view.log;\n\nimport com.calendarfx.google.view.log.ActionType;\nimport com.calendarfx.google.view.log.LogItem;\nimport com.calendarfx.google.view.log.LogPane;\nimport com.calendarfx.google.view.log.StatusType;\nimport com.google.api.client.util.Lists;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.event.Event;\nimport javafx.geometry.Side;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.ToolBar;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.layout.BorderPane;\nimport org.controlsfx.control.MasterDetailPane;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Skin for the log pane.\n *\n * Created by gdiaz on 22/02/2017.\n */\npublic class LogPaneSkin extends SkinBase<LogPane> {\n\n    /**\n     * Constructor for all SkinBase instances.\n     *\n     * @param control The control for which this Skin should attach to.\n     */\n    public LogPaneSkin(LogPane control, TableView<LogItem> table) {\n        super(control);\n\n        TextArea textArea = new TextArea();\n        textArea.addEventFilter(KeyEvent.ANY, Event::consume);\n\n        BorderPane tablePane = new BorderPane();\n        tablePane.setTop(createToolBar());\n        tablePane.setCenter(table);\n\n        MasterDetailPane container = new MasterDetailPane();\n        container.setMasterNode(tablePane);\n        container.setDetailNode(textArea);\n        container.setDetailSide(Side.RIGHT);\n        container.setDividerPosition(0.65);\n        container.setShowDetailNode(false);\n\n        control.getSelectedItems().addListener((Observable obs) -> {\n            List<LogItem> item = control.getSelectedItems();\n            if (item == null || item.isEmpty() || item.size() > 1 || item.get(0).getException() == null) {\n                textArea.setText(null);\n                container.setShowDetailNode(false);\n            } else {\n                StringWriter stackTraceWriter = new StringWriter();\n                item.get(0).getException().printStackTrace(new PrintWriter(stackTraceWriter));\n                textArea.setText(stackTraceWriter.toString());\n                container.setShowDetailNode(true);\n            }\n        });\n\n        getChildren().add(container);\n    }\n\n    private ToolBar createToolBar() {\n        Button clearAllBtn = new Button();\n        clearAllBtn.setText(\"Clear All\");\n        clearAllBtn.setGraphic(new FontIcon(FontAwesome.TRASH));\n        clearAllBtn.setOnAction(evt -> getSkinnable().clearItems());\n\n        Button clearSelectionBtn = new Button();\n        clearSelectionBtn.setText(\"Clear Selected\");\n        clearSelectionBtn.setGraphic(new FontIcon(FontAwesome.TRASH_O));\n        clearSelectionBtn.setOnAction(evt -> getSkinnable().removeItems(Lists.newArrayList(getSkinnable().getSelectedItems())));\n\n        List<ToggleButton> statusTypeButtons = new ArrayList<>();\n\n        InvalidationListener statusListener = obs -> {\n            List<StatusType> statuses = Lists.newArrayList();\n            for (ToggleButton btn : statusTypeButtons) {\n                if (btn.isSelected()) {\n                    statuses.add((StatusType) btn.getUserData());\n                }\n            }\n            getSkinnable().filter(statuses);\n        };\n\n\n        for (StatusType type : StatusType.values()) {\n            ToggleButton btn = new ToggleButton();\n            btn.setSelected(true);\n            btn.setGraphic(type.createView());\n            btn.setTooltip(new Tooltip(type.getDisplayName()));\n            btn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n            btn.setUserData(type);\n            btn.selectedProperty().addListener(statusListener);\n            statusTypeButtons.add(btn);\n        }\n\n        TextField textField = new TextField();\n        textField.setPrefColumnCount(20);\n        textField.setPromptText(\"Search: Calendar or Description\");\n        textField.addEventHandler(KeyEvent.KEY_PRESSED, evt -> {\n            if (evt.getCode() == KeyCode.ENTER) {\n                getSkinnable().filter(textField.getText());\n            }\n        });\n        Button searchBtn = new Button();\n        searchBtn.setGraphic(new FontIcon(FontAwesome.SEARCH));\n        searchBtn.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n        searchBtn.setOnAction(evt -> getSkinnable().filter(textField.getText()));\n\n        ComboBox<ActionTypeWrapper> actionTypeComboBox = new ComboBox<>();\n        actionTypeComboBox.getItems().addAll(ActionTypeWrapper.values());\n        actionTypeComboBox.setEditable(false);\n        actionTypeComboBox.valueProperty().addListener(obs -> getSkinnable().filter(actionTypeComboBox.getValue().getActionType()));\n\n        ToolBar toolBar = new ToolBar();\n        toolBar.getItems().add(clearAllBtn);\n        toolBar.getItems().add(clearSelectionBtn);\n        toolBar.getItems().add(new Separator());\n        toolBar.getItems().addAll(statusTypeButtons);\n        toolBar.getItems().add(new Separator());\n        toolBar.getItems().add(new Label(\"Action\"));\n        toolBar.getItems().add(actionTypeComboBox);\n        toolBar.getItems().add(new Separator());\n        toolBar.getItems().add(textField);\n        toolBar.getItems().add(searchBtn);\n\n        return toolBar;\n    }\n\n    private static class ActionTypeWrapper {\n\n        private static final ActionTypeWrapper NULL_ITEM = new ActionTypeWrapper();\n\n        private ActionType actionType;\n\n        ActionTypeWrapper() {\n            super();\n        }\n\n        ActionTypeWrapper(ActionType actionType) {\n            this();\n            this.actionType = actionType;\n        }\n\n        ActionType getActionType() {\n            return actionType;\n        }\n\n        static List<ActionTypeWrapper> values() {\n            List<ActionTypeWrapper> values = Lists.newArrayList();\n            values.add(NULL_ITEM);\n            for (ActionType actionType : ActionType.values()) {\n                values.add(new ActionTypeWrapper(actionType));\n            }\n            return values;\n        }\n\n        @Override\n        public String toString() {\n            return actionType == null ? \"\" : actionType.getDisplayName();\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/main/java/module-info.java",
    "content": "module com.calendarfx.google {\n    requires transitive javafx.graphics;\n\n    requires org.kordamp.ikonli.javafx;\n    requires org.kordamp.ikonli.fontawesome;\n    requires org.kordamp.ikonli.core;\n\n    requires com.calendarfx.view;\n    requires javafx.base;\n    requires com.google.api.services.calendar;\n    requires org.controlsfx.controls;\n    requires javafx.controls;\n    requires javafx.web;\n    requires geocoder.java;\n    requires com.dlsc.gmapsfx;\n    requires com.google.common;\n    requires com.google.api.client;\n    requires com.google.api.client.auth;\n    requires com.google.api.services.oauth2;\n    requires com.google.api.client.json.jackson2;\n    requires google.api.client;\n\n    exports com.calendarfx.google;\n}"
  },
  {
    "path": "CalendarFXGoogle/src/main/resources/com/calendarfx/google/service/client-secrets.json",
    "content": "{\n  \"installed\": {\n    \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n    \"client_secret\": \"Tnwk7IGPlvnnn7TQ-WbvpWv3\",\n    \"token_uri\": \"https://accounts.google.com/o/oauth2/token\",\n    \"client_email\": \"\",\n    \"redirect_uris\": [\n      \"urn:ietf:wg:oauth:2.0:oob\",\n      \"oob\"\n    ],\n    \"client_x509_cert_url\": \"\",\n    \"client_id\": \"972996368401-vlm70ls1ivnpvlfu80odn880bhne1pnc.apps.googleusercontent.com\",\n    \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\"\n  }\n}"
  },
  {
    "path": "CalendarFXGoogle/src/main/resources/com/calendarfx/google/view/popover/google-popover.css",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n\n.title {\n    -fx-font-weight: bold;\n}\n\n.button-icon {\n    -fx-cursor: hand;\n}\n\n.link {\n    -fx-cursor: hand;\n    -fx-underline: true;\n}\n\n.details-view .notification-item {\n    -fx-spacing: 5;\n    -fx-padding: 0 0 5 0;\n}\n\n.attendees-view .top {\n    -fx-spacing: 5;\n    -fx-padding: 0 0 5 0;\n}\n\n.attendees-view .center {\n    -fx-spacing: 5;\n    -fx-padding: 0 0 10 0;\n    -fx-fill-width: true;\n    -fx-fit-to-width: true;\n}\n\n.attendees-view .scroll-pane {\n    -fx-padding: 5;\n    -fx-background-color: transparent;\n    -fx-border-color: transparent;\n    -fx-fit-to-width: true;\n}\n\n.attendees-view .bottom {\n    -fx-spacing: 5;\n    -fx-padding: 5;\n}\n\n.attendees-view .bottom .checks-parent {\n    -fx-spacing: 10;\n}\n\n.attendees-view .email-field {\n    -fx-text-fill: black;\n}\n\n.attendees-view .email-field:invalid {\n    -fx-text-fill: red;\n}\n\n.attendee-item {\n    -fx-spacing: 5;\n    -fx-padding: 5;\n    -fx-background-color: #EEE;\n}\n\n.popover-footer {\n    -fx-padding: 0.0 !important;\n}\n\n.map-placeholder {\n    -fx-padding: 10.0;\n}\n\n.map-view-wrapper {\n    -fx-padding: 0 20 20 20;\n}\n\n.map {\n    -fx-border-color: gray;\n}\n"
  },
  {
    "path": "CalendarFXGoogle/src/test/java/com/calendarfx/google/view/popover/HelloGoogleEntryPopOverContentPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.google.view.popover;\n\nimport com.calendarfx.google.model.GoogleEntry;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DayView;\nimport javafx.application.Application;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\n/**\n * Sample for the google entry pop over.\n *\n * Created by gdiaz on 13/01/2017.\n */\npublic class HelloGoogleEntryPopOverContentPane extends Application {\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        Calendar calendar = new Calendar();\n        calendar.setName(\"Google Calendar\");\n        calendar.setStyle(Calendar.Style.STYLE2);\n\n        GoogleEntry entry = new GoogleEntry();\n        entry.setTitle(\"Google Entry\");\n        entry.setCalendar(calendar);\n        entry.setLocation(\"Bogota\");\n\n        ObservableList<Calendar> allCalendars = FXCollections.observableArrayList(calendar);\n\n        DayView dayView = new DayView();\n        GoogleEntryPopOverContentPane pane = new GoogleEntryPopOverContentPane(entry, allCalendars, dayView);\n\n        primaryStage.setTitle(\"Google Calendar\");\n        Scene scene = new Scene(pane, 400, 600);\n        scene.getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n        primaryStage.setScene(scene);\n        primaryStage.sizeToScene();\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n}\n"
  },
  {
    "path": "CalendarFXResourceApp/.gitignore",
    "content": "/target\n*.iml\n"
  },
  {
    "path": "CalendarFXResourceApp/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>resources</artifactId>\n    <name>CalendarFXResourceApp</name>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.resource.app.ResourceCalendarApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "CalendarFXResourceApp/src/main/java/com/calendarfx/resource/app/ResourceCalendarApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.resource.app;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Marker;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.EntryViewBase;\nimport com.calendarfx.view.ResourceCalendarView;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.util.Random;\nimport java.util.function.Supplier;\n\npublic class ResourceCalendarApp extends Application {\n\n    public static final int DATA_GENERATION_SEED = 11011;\n\n    final Random random = new Random(DATA_GENERATION_SEED);\n\n    @Override\n    public void start(Stage primaryStage) {\n\n        ResourceCalendarView<String> resourceCalendarView = new ResourceCalendarView<>();\n\n        resourceCalendarView.setContextMenuCallback(param -> {\n            ContextMenu menu = new ContextMenu();\n            MenuItem newMarkerItem = new MenuItem(\"New Marker\");\n            newMarkerItem.setOnAction(evt -> {\n                Marker marker = new Marker();\n                marker.setStyle(\"-fx-background-color: red;\");\n                marker.setTitle(\"New Marker\");\n                marker.setTime(param.getZonedDateTime());\n                resourceCalendarView.getMarkers().add(marker);\n            });\n            menu.getItems().add(newMarkerItem);\n            return menu;\n        });\n\n        resourceCalendarView.setHeaderFactory(resource -> {\n            Label label1 = new Label(\"IG-TR\");\n            Label label2 = new Label(\"29.000\");\n            Label label3 = new Label(\"13.200\");\n            Label label4 = new Label(\"92 (CPC)\");\n\n            label1.setStyle(\"-fx-font-family: Monospaced; -fx-font-style: bold;\");\n            label2.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n            label3.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n            label4.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n\n            label1.setAlignment(Pos.CENTER);\n            label2.setAlignment(Pos.CENTER);\n            label3.setAlignment(Pos.CENTER);\n            label4.setAlignment(Pos.CENTER);\n\n            VBox box = new VBox(5, label1, label2, label3, label4);\n            box.setPadding(new Insets(10));\n            box.setFillWidth(true);\n            return box;\n        });\n\n        for (int i = 0; i < 5; i++) {\n            CalendarSource source = new CalendarSource(\"Default\");\n\n            HelloDayViewCalendar calendar1 = new HelloDayViewCalendar(random.nextLong());\n            calendar1.generateBaseEntries();\n            calendar1.setStyle(Style.STYLE1);\n            source.getCalendars().add(calendar1);\n\n            HelloDayViewCalendar calendar2 = new HelloDayViewCalendar(random.nextLong());\n            calendar2.generateBaseEntries();\n            calendar2.setStyle(Style.STYLE2);\n            source.getCalendars().add(calendar2);\n\n            HelloDayViewCalendar calendar3 = new HelloDayViewCalendar(random.nextLong());\n            calendar3.generateBaseEntries();\n            calendar3.setStyle(Style.STYLE3);\n            source.getCalendars().add(calendar3);\n\n            HelloDayViewCalendar calendar4 = new HelloDayViewCalendar(random.nextLong());\n            calendar4.generateTopEntries();\n            calendar4.setStyle(Style.STYLE4);\n            source.getCalendars().add(calendar4);\n\n            String resource = \"Resource \" + (i + 1);\n            resourceCalendarView.getResources().add(resource);\n\n            DayView dayView = resourceCalendarView.getDayView(resource);\n            dayView.getCalendarSources().setAll(source);\n            dayView.setEnableCurrentTimeMarker(true);\n            dayView.setEnableCurrentTimeCircle(i == 0);\n\n            /* PSI:\n             * Setting a custom entry view factory will allow you to set icons on your entry\n             * views based on state information provided by your model.\n             */\n\n            Random iconsRandom = new Random();\n\n            dayView.setEntryViewFactory(entry -> {\n                iconsRandom.setSeed(entry.getTitle().hashCode());\n\n                DayEntryView entryView = new DayEntryView(entry);\n\n                if (entry instanceof TopEntry) {\n                    entryView.setWidthPercentage(25.0);\n                    entryView.setAlignmentStrategy(EntryViewBase.AlignmentStrategy.ALIGN_RIGHT);\n                    entryView.setLayer(DateControl.Layer.TOP);\n                }\n\n                /* PSI:\n                 * Here you can experiment with the new alignment strategy that allows\n                 * applications to have entry views show up on the left, the center, or\n                 * the right of a day view with a given width. This is needed to support\n                 * \"vertical bars\".\n                 */\n                // entryView.setPrefWidth(10);\n                // entryView.setAlignmentStrategy(AlignmentStrategy.ALIGN_LEFT);\n\n                /* PSI:\n                 * Here you can experiment with the new height layout strategy that allows\n                 * applications to have entry views show up with their preferred height instead\n                 * of a height determined by their start and end time. This is required to\n                 * implement the Event Monitoring Panel.\n                 */\n                // entryView.setHeightLayoutStrategy(HeightLayoutStrategy.COMPUTE_PREF_SIZE);\n\n                if (iconsRandom.nextDouble() > .7) {\n                    final FontIcon node = new FontIcon(FontAwesome.ERASER);\n                    node.setIconColor(Color.RED);\n                    node.setIconSize(16);\n                    entryView.addNode(Pos.BOTTOM_RIGHT, node);\n                }\n\n                if (iconsRandom.nextDouble() > .9) {\n                    final FontIcon node = new FontIcon(FontAwesome.CODE);\n                    node.setIconColor(Color.BLUE);\n                    node.setIconSize(16);\n                    entryView.addNode(Pos.BOTTOM_RIGHT, node);\n                }\n\n                if (iconsRandom.nextDouble() > .7) {\n                    final FontIcon node = new FontIcon(FontAwesome.QRCODE);\n                    node.setIconColor(Color.MEDIUMPURPLE);\n                    node.setIconSize(16);\n                    entryView.addNode(Pos.BOTTOM_RIGHT, node);\n                }\n\n                if (iconsRandom.nextDouble() > .7) {\n                    final FontIcon node = new FontIcon(FontAwesome.SIGN_IN);\n                    node.setIconColor(Color.MEDIUMSPRINGGREEN);\n                    node.setIconSize(16);\n                    entryView.addNode(Pos.TOP_RIGHT, node);\n                }\n\n                return entryView;\n            });\n        }\n\n        Marker marker1 = new Marker();\n        marker1.setTitle(\"My Marker 1\");\n        marker1.setTime(ZonedDateTime.now().minusHours(1));\n        marker1.setMovable(false);\n        resourceCalendarView.getMarkers().add(marker1);\n\n        Marker marker2 = new Marker();\n        marker2.setTitle(\"My Marker 2\");\n        marker2.setTime(ZonedDateTime.now().plusHours(1));\n        marker2.setMovable(false);\n        marker2.getStyleClass().add(\"marker2\");\n        resourceCalendarView.getMarkers().add(marker2);\n\n\n        StackPane stackPane = new StackPane();\n        stackPane.getChildren().addAll(resourceCalendarView);\n\n        Thread updateTimeThread = new Thread(\"Calendar: Update Time Thread\") {\n            @Override\n            public void run() {\n                while (true) {\n                    Platform.runLater(() -> {\n                        resourceCalendarView.setToday(LocalDate.now());\n                        resourceCalendarView.setTime(LocalTime.now());\n                    });\n\n                    try {\n                        // update every 10 seconds\n                        sleep(10000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n\n                }\n            }\n        };\n\n        updateTimeThread.setPriority(Thread.MIN_PRIORITY);\n        updateTimeThread.setDaemon(true);\n        updateTimeThread.start();\n\n        Scene scene = new Scene(stackPane);\n        primaryStage.setTitle(\"Calendar\");\n        primaryStage.setScene(scene);\n        primaryStage.setWidth(1300);\n        primaryStage.setHeight(1000);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        final Random dataRandom = new Random();\n\n        public HelloDayViewCalendar(long dataSeed) {\n            dataRandom.setSeed(dataSeed);\n        }\n\n        public void generateBaseEntries() {\n            createEntries(LocalDate.now().minusDays(2), Entry::new);\n            createEntries(LocalDate.now().minusDays(1), Entry::new);\n            createEntries(LocalDate.now(), Entry::new);\n            createEntries(LocalDate.now().plusDays(1), Entry::new);\n            createEntries(LocalDate.now().plusDays(2), Entry::new);\n        }\n\n        public void generateTopEntries() {\n            createEntries(LocalDate.now(), TopEntry::new);\n        }\n\n        private <T extends Entry<?>> void createEntries(LocalDate startDate, Supplier<T> entryProducer) {\n            for (int j = 0; j < 5 + (int) (dataRandom.nextDouble() * 4); j++) {\n                T entry = entryProducer.get();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                String s = entry.getClass().getSimpleName();\n                entry.setTitle(s + (j + 1));\n\n                int hour = (int) (dataRandom.nextDouble() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (dataRandom.nextDouble() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n    public static class TopEntry<T> extends Entry<T>\n    {\n    }\n}\n"
  },
  {
    "path": "CalendarFXResourceApp/src/main/java/com/calendarfx/resource/app/ResourceCalendarAppLauncher.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.resource.app;\n\npublic class ResourceCalendarAppLauncher {\n\n    public static void main(String[] args) {\n        System.setProperty(\"calendarfx.developer\", \"true\");\n        ResourceCalendarApp.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXResourceApp/src/main/java/module-info.java",
    "content": "module com.calendarfx.resource.app {\n    requires transitive javafx.graphics;\n\n    requires javafx.controls;\n    requires com.calendarfx.view;\n\n    exports com.calendarfx.resource.app;\n}"
  },
  {
    "path": "CalendarFXSampler/.gitignore",
    "content": "/target\n*.iml\n"
  },
  {
    "path": "CalendarFXSampler/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~  Copyright (C) 2006 Google Inc.\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>sampler</artifactId>\n    <packaging>jar</packaging>\n\n    <name>CalendarFXSampler</name>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n        <maven.javadoc.skip>true</maven.javadoc.skip>\n    </properties>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-base</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-fxml</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-graphics</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-controls</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>application</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.controlsfx</groupId>\n            <artifactId>fxsampler</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.controlsfx</groupId>\n            <artifactId>controlsfx</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.demo.CalendarFXSampler</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/CalendarFXDateControlSample.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo;\n\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DeveloperConsole;\nimport javafx.geometry.Pos;\nimport javafx.geometry.Side;\nimport javafx.scene.Node;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\nimport org.controlsfx.control.MasterDetailPane;\n\nimport static java.util.Objects.requireNonNull;\n\npublic abstract class CalendarFXDateControlSample extends CalendarFXSample {\n\n    @Override\n    public final Node getPanel(Stage stage) {\n        DateControl dateControl = createControl();\n        control = dateControl;\n        requireNonNull(control, \"missing date control\");\n\n        DeveloperConsole console = new DeveloperConsole();\n        console.setDateControl(dateControl);\n\n        if (isSupportingDeveloperConsole()) {\n            MasterDetailPane masterDetailPane = new MasterDetailPane();\n            masterDetailPane.setMasterNode(wrap(dateControl));\n            masterDetailPane.setDetailSide(Side.BOTTOM);\n            masterDetailPane.setDetailNode(console);\n            masterDetailPane.setShowDetailNode(true);\n            return masterDetailPane;\n        }\n\n        return wrap(dateControl);\n    }\n\n    @Override\n    protected Node wrap(Node node) {\n        StackPane outerPane = new StackPane();\n        outerPane.setStyle(\"-fx-padding: 20px;\");\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 20px;\");\n        outerPane.getChildren().add(stackPane);\n\n        StackPane.setAlignment(node, Pos.CENTER);\n        stackPane.getChildren().add(node);\n\n        return outerPane;\n    }\n\n    protected boolean isSupportingDeveloperConsole() {\n        return true;\n    }\n\n    @Override\n    protected abstract DateControl createControl();\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/CalendarFXSample.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo;\n\nimport atlantafx.base.theme.NordDark;\nimport com.calendarfx.util.CalendarFX;\nimport com.calendarfx.view.CalendarFXControl;\nimport fr.brouillard.oss.cssfx.CSSFX;\nimport fxsampler.SampleBase;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport static java.util.Objects.requireNonNull;\n\npublic abstract class CalendarFXSample extends SampleBase {\n\n    protected Node control;\n\n    {\n        setUserAgentStylesheet(new NordDark().getUserAgentStylesheet());\n    }\n\n    @Override\n    public Node getPanel(Stage stage) {\n        control = createControl();\n        requireNonNull(control, \"missing date control\");\n        CSSFX.start();\n        return wrap(control);\n    }\n\n    protected Node wrap(Node node) {\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 20px;\");\n        stackPane.getChildren().add(node);\n\n        HBox box = new HBox();\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n        box.getChildren().add(stackPane);\n\n        return box;\n    }\n\n    @Override\n    public String getProjectName() {\n        return \"CalendarFX\";\n    }\n\n    @Override\n    public String getProjectVersion() {\n        return CalendarFX.getVersion();\n    }\n\n    @Override\n    public double getControlPanelDividerPosition() {\n        return .7;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        if (control instanceof CalendarFXControl) {\n            return new CalendarPropertySheet(((CalendarFXControl) control).getPropertySheetItems());\n        }\n        return null;\n    }\n\n    @Override\n    public String getControlStylesheetURL() {\n        return \"/calendar.css\";\n    }\n\n    @Override\n    public final String getSampleSourceURL() {\n        return getSampleSourceBase() + getClass().getSimpleName() + \".java\";\n    }\n\n    private final String getSampleSourceBase() {\n        return \"http://dlsc.com/wp-content/html/calendarfx/sampler/\";\n    }\n\n    // Javadoc support.\n\n    protected Class<?> getJavaDocClass() {\n        return null;\n    }\n\n    private String getJavaDocBase() {\n        return \"http://dlsc.com/wp-content/html/calendarfx/apidocs/\";\n    }\n\n    @Override\n    public final String getJavaDocURL() {\n        Class<?> cl = getJavaDocClass();\n        String url;\n        if (cl == null) {\n            url = getJavaDocBase() + \"index.html?sampler=true\";\n        } else {\n            url = getJavaDocBase() + cl.getName().replace(\".\", \"/\") + \".html\";\n        }\n        return url;\n    }\n\n    protected abstract Node createControl();\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/CalendarFXSampler.java",
    "content": "/**\n * Copyright (C) 2014 - 2021 DLSC Software & Consulting GmbH (dlsc.com)\n *\n * This file is part of FlexGanttFX.\n */\npackage com.calendarfx.demo;\n\nimport fxsampler.FXSampler;\n\npublic class CalendarFXSampler {\n\n    public static void main(String[] args) {\n        FXSampler.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/CalendarFXSamplerProject.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo;\n\nimport fxsampler.FXSamplerProject;\nimport fxsampler.model.WelcomePage;\n\npublic class CalendarFXSamplerProject implements FXSamplerProject {\n\n    @Override\n    public String getProjectName() {\n        return \"CalendarFX\";\n    }\n\n    @Override\n    public String getSampleBasePackage() {\n        return \"com.calendarfx.demo\";\n    }\n\n    @Override\n    public WelcomePage getWelcomePage() {\n        return new CalendarFXSamplerWelcome();\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/CalendarFXSamplerWelcome.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo;\n\nimport fxsampler.model.WelcomePage;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Label;\nimport javafx.scene.text.TextAlignment;\n\npublic class CalendarFXSamplerWelcome extends WelcomePage {\n\n    public CalendarFXSamplerWelcome() {\n        super(\"CalendarFX\", new Label(\"\"));\n\n        Label label = (Label) getContent();\n        label.setWrapText(true);\n        label.setMaxWidth(Double.MAX_VALUE);\n        label.setMaxHeight(Double.MAX_VALUE);\n        label.setTextAlignment(TextAlignment.CENTER);\n        label.setAlignment(Pos.CENTER);\n        label.setPadding(new Insets(50));\n        label.setText(\"Welcome to the CalendarFX sampler. This application allows you to quickly browse through the \"\n                + \"various controls that are available in this framework. In each sample you can play around with the \"\n                + \"properties and controls shown on the right-hand side.\");\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/entries/HelloAllDayEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.entries;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.AllDayEntryView;\nimport com.calendarfx.view.EntryViewBase;\n\npublic class HelloAllDayEntryView extends HelloEntryViewBase {\n\n    @Override\n    public String getSampleName() {\n        return \"All Day Entry View\";\n    }\n\n    @Override\n    protected EntryViewBase<?> createEntryView(Entry<?> entry) {\n        AllDayEntryView view = new AllDayEntryView(entry);\n        view.setPrefSize(400, 20);\n        return view;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"This view is used to display a single entry in an all day view.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return AllDayEntryView.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/entries/HelloDayEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.entries;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.EntryViewBase;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.layout.Region;\n\npublic class HelloDayEntryView extends HelloEntryViewBase {\n\n    @Override\n    public String getSampleName() {\n        return \"Day Entry View\";\n    }\n\n    @Override\n    protected EntryViewBase<?> createEntryView(Entry<?> entry) {\n        DayEntryView view = new DayEntryView(entry);\n        ContextMenu menu = new ContextMenu();\n        for (Pos pos : Pos.values()) {\n            MenuItem item = new MenuItem(pos.name());\n            item.setOnAction(evt -> {\n                view.clearNodes();\n\n                for (int i = 0; i < 3; i++) {\n                    Region region = new Region();\n\n                    switch (i) {\n                        case 0:\n                            region.setStyle(\"-fx-background-color: orange;\");\n                            break;\n                        case 1:\n                            region.setStyle(\"-fx-background-color: yellow;\");\n                            break;\n                        case 2:\n                            region.setStyle(\"-fx-background-color: blue;\");\n                            break;\n                    }\n\n                    region.setPrefSize(8, 8);\n                    view.addNode(pos, region);\n                }\n\n            });\n\n            menu.getItems().add(item);\n        }\n\n        view.setContextMenu(menu);\n        view.setPrefSize(200, 300);\n        return view;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"This view is used to display a single entry in a day view.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayEntryView.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/entries/HelloEntryViewBase.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.entries;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.EntryViewBase;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\nimport org.controlsfx.control.PropertySheet;\n\npublic abstract class HelloEntryViewBase extends CalendarFXSample {\n\n    protected EntryViewBase<?> entryView;\n    protected Entry<?> entry;\n\n    public HelloEntryViewBase() {\n        Calendar calendar = new Calendar(\"Test Calendar\");\n        entry = new Entry<>(\"Test Entry\");\n        entry.setCalendar(calendar);\n    }\n\n    @Override\n    protected Node createControl() {\n        entryView = createEntryView(entry);\n        control = entryView;\n        return entryView;\n    }\n\n    protected abstract EntryViewBase<?> createEntryView(Entry<?> entry);\n\n    @Override\n    public Node getControlPanel() {\n        PropertySheet sheet = new CalendarPropertySheet(entryView.getPropertySheetItems());\n        sheet.getItems().addAll(entry.getPropertySheetItems());\n        return sheet;\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/entries/HelloMonthEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.entries;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.EntryViewBase;\nimport com.calendarfx.view.MonthEntryView;\n\nimport java.time.LocalDate;\n\npublic class HelloMonthEntryView extends HelloEntryViewBase {\n\n    public HelloMonthEntryView() {\n        super();\n\n        entry.setInterval(LocalDate.now(), LocalDate.now().plusDays(5));\n    }\n\n    @Override\n    protected EntryViewBase<?> createEntryView(Entry<?> entry) {\n        MonthEntryView view = new MonthEntryView(entry);\n        view.setPrefSize(400, 20);\n        return view;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Month Entry View\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return MonthEntryView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"This view is used to display a single entry in a month view.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/pages/HelloDayPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.pages;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.page.DayPage;\n\npublic class HelloDayPage extends CalendarFXDateControlSample {\n\n    private DayPage dayPage;\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource(\"My Calendars\");\n        final Calendar calendar = new Calendar(\"Calendar\");\n        calendar.setShortName(\"C\");\n        calendar.setStyle(Style.STYLE2);\n        calendarSource.getCalendars().add(calendar);\n\n        dayPage = new DayPage();\n        dayPage.getCalendarSources().add(calendarSource);\n\n        return dayPage;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Day Page\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day page displays the calendar information for a single day.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayPage.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/pages/HelloMonthPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.pages;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.page.MonthPage;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.YearMonth;\n\npublic class HelloMonthPage extends CalendarFXDateControlSample {\n\n    private MonthPage monthPage;\n\n    @Override\n    protected DateControl createControl() {\n        monthPage = new MonthPage();\n\n        CalendarSource calendarSource = new CalendarSource();\n        HelloCalendar calendar1 = new HelloCalendar(monthPage.getMonthView().getYearMonth());\n        HelloCalendar calendar2 = new HelloCalendar(monthPage.getMonthView().getYearMonth());\n        HelloCalendar calendar3 = new HelloCalendar(monthPage.getMonthView().getYearMonth());\n        HelloCalendar calendar4 = new HelloCalendar(monthPage.getMonthView().getYearMonth());\n\n        calendar1.setStyle(Style.STYLE1);\n        calendar2.setStyle(Style.STYLE2);\n        calendar3.setStyle(Style.STYLE3);\n        calendar4.setStyle(Style.STYLE4);\n\n        calendarSource.getCalendars().addAll(calendar1, calendar2, calendar3, calendar4);\n\n        monthPage.getCalendarSources().add(calendarSource);\n\n        return monthPage;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Month Page\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return MonthPage.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The month page displays the calendar information for a full month.\";\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar(YearMonth month) {\n            createEntries(month);\n        }\n\n        private void createEntries(YearMonth month) {\n            for (int i = 1; i < 28; i++) {\n\n                LocalDate date = month.atDay(i);\n\n                for (int j = 0; j < (int) (Math.random() * 7); j++) {\n                    Entry<?> entry = new Entry<>();\n\n                    entry.setTitle(\"Entry \" + (j + 1));\n\n                    int hour = (int) (Math.random() * 21);\n                    int durationInHours = Math.min(24 - hour, (int) (Math.random() * 4));\n\n                    LocalTime startTime = LocalTime.of(hour, 0);\n                    LocalTime endTime = startTime.plusHours(durationInHours);\n\n                    entry.setInterval(date, startTime, date.plusDays((int) (Math.random() * 4)), endTime);\n\n                    if (Math.random() < .3) {\n                        entry.setFullDay(true);\n                    }\n\n                    entry.setCalendar(this);\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/pages/HelloWeekPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.pages;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.page.WeekPage;\n\npublic class HelloWeekPage extends CalendarFXDateControlSample {\n\n    private WeekPage weekPage;\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource(\"My Calendars\");\n        calendarSource.getCalendars().add(new Calendar(\"Test\"));\n\n        weekPage = new WeekPage();\n        weekPage.getCalendarSources().add(calendarSource);\n\n        return weekPage;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return WeekPage.class;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Week Page\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The week page displays a week view.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/pages/HelloYearPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.pages;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.page.YearPage;\n\npublic class HelloYearPage extends CalendarFXDateControlSample {\n\n    private YearPage yearPage;\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource(\"My Calendars\");\n        calendarSource.getCalendars().add(new Calendar(\"Test\"));\n\n        yearPage = new YearPage();\n        yearPage.getCalendarSources().add(calendarSource);\n\n        return yearPage;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Year Page\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return YearPage.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The year page displays the calendar information for a full year.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/performance/HelloPerformance.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.performance;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.geometry.Orientation;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Separator;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloPerformance extends CalendarFXDateControlSample {\n\n    private CalendarView calendarView;\n    private ComboBox<Integer> comboBox;\n    private Label label;\n    private HelloCalendar calendar;\n    private int style;\n\n    @Override\n    public String getSampleName() {\n        return \"Performance\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource(\"My Calendars\");\n        calendarSource.getCalendars().add(calendar = new HelloCalendar());\n\n        calendarView = new CalendarView();\n        calendarView.getCalendarSources().add(calendarSource);\n\n        return calendarView;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        VBox vBox = new VBox();\n        vBox.setFillWidth(true);\n        vBox.setSpacing(10);\n\n        // button\n        Button button = new Button(\"Create Entries\");\n        button.setMaxWidth(Double.MAX_VALUE);\n        button.setOnAction(evt -> createEntries());\n        VBox.setVgrow(button, Priority.NEVER);\n        vBox.getChildren().add(button);\n\n        // box\n        comboBox = new ComboBox<>();\n        comboBox.getItems().addAll(100, 1000, 2000, 3000, 10000, 100000, 1000000);\n        comboBox.getSelectionModel().select(0);\n        comboBox.setMaxWidth(Double.MAX_VALUE);\n        vBox.getChildren().add(comboBox);\n\n        // label\n        label = new Label(\"Time: \");\n        vBox.getChildren().add(label);\n\n        // Separator\n        Separator separator = new Separator(Orientation.HORIZONTAL);\n        vBox.getChildren().add(separator);\n\n        // sheet\n        CalendarPropertySheet sheet = new CalendarPropertySheet(calendarView.getPropertySheetItems());\n        VBox.setVgrow(sheet, Priority.ALWAYS);\n        vBox.getChildren().add(sheet);\n\n        return vBox;\n    }\n\n    public void createEntries() {\n        calendar.setStyle(Calendar.Style.getStyle(style++));\n        calendar.clear();\n\n        LocalTime dailyStartTime = LocalTime.of(8, 0);\n        LocalTime dailyEndTime = LocalTime.of(20, 0);\n\n        LocalDate entryDate = LocalDate.now();\n        LocalTime entryTime = dailyStartTime;\n\n        long startTime = System.currentTimeMillis();\n        int count = comboBox.getValue();\n\n        calendar.startBatchUpdates();\n\n        for (int i = 0; i < count; i++) {\n            Entry<String> entry = new Entry<>(\"Entry \" + i);\n            entry.setInterval(new Interval(entryDate, entryTime, entryDate, entryTime.plusMinutes(30)));\n            entryTime = entryTime.plusHours(1);\n            if (entryTime.isAfter(dailyEndTime)) {\n                entryDate = entryDate.plusDays(1);\n                entryTime = dailyStartTime;\n            }\n\n            entry.setCalendar(calendar);\n        }\n\n        calendar.stopBatchUpdates();\n\n        label.setText(\"Time: \" + (System.currentTimeMillis() - startTime));\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A test sample to confirm high performance even when dealing with a lot of entries.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return Calendar.class;\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar() {\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/popover/HelloEntryDetailsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.popover;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.popover.EntryDetailsView;\nimport javafx.scene.Node;\n\npublic class HelloEntryDetailsView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Entry Details\";\n    }\n\n    @Override\n    protected Node createControl() {\n        DayView dayView = new DayView();\n        Entry<String> entry = new Entry<>(\"Hello Entry\");\n        entry.setCalendar(new Calendar(\"Dummy Calendar\"));\n        return new EntryDetailsView(entry, dayView);\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return EntryDetailsView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A view used to edit various properties of a calendar entry.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/popover/HelloEntryHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.popover;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.popover.EntryHeaderView;\nimport javafx.scene.Node;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class HelloEntryHeaderView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Entry Header View\";\n    }\n\n    @Override\n    protected Node createControl() {\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        List<Calendar> calendars = new ArrayList<>();\n        calendars.add(meetings);\n        calendars.add(training);\n        calendars.add(customers);\n        calendars.add(holidays);\n\n        Entry<String> entry = new Entry<>(\"Hello Header View\");\n        entry.setCalendar(meetings);\n\n        return new EntryHeaderView(entry, calendars);\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return EntryHeaderView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A view used to select a calendar from a list of calendars.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/popover/HelloPopOverContentPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.popover;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.popover.EntryPropertiesView;\nimport com.calendarfx.view.popover.PopOverContentPane;\nimport com.calendarfx.view.popover.PopOverTitledPane;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\npublic class HelloPopOverContentPane extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        Calendar calendar = new Calendar();\n        calendar.setName(\"Calendar\");\n        calendar.setStyle(Calendar.Style.STYLE2);\n\n        Entry entry = new Entry<>();\n        entry.setTitle(\"Google Entry\");\n        entry.setCalendar(calendar);\n        entry.setLocation(\"Bogota\");\n\n        PopOverContentPane pane = new PopOverContentPane();\n\n        EntryPropertiesView entryPropertiesView = new EntryPropertiesView(entry);\n        PopOverTitledPane titledPane = new PopOverTitledPane(\"Properties\", entryPropertiesView);\n        titledPane.getStyleClass().add(\"no-padding\");\n\n        titledPane.setExpanded(true);\n\n        pane.getPanes().add(titledPane);\n\n        primaryStage.setTitle(\"Entry Properties\");\n        Scene scene = new Scene(pane, 400, 600);\n        scene.getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n        primaryStage.setScene(scene);\n        primaryStage.sizeToScene();\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloOptionsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.print.OptionsView;\nimport javafx.scene.Node;\n\npublic class HelloOptionsView extends CalendarFXSample {\n\n    private final OptionsView view = new OptionsView();\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return OptionsView.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        return view;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Options View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A control that allows to change a few basic settings before printing a calendar view.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloPaperView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.print.PaperView;\nimport javafx.scene.Node;\n\npublic class HelloPaperView extends CalendarFXSample {\n\n    @Override\n    protected Node createControl() {\n        return new PaperView();\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return PaperView.class;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Paper View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"This control allows to select the view that is going to be printed and configure the paper type and print margins.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloPreviewPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.print.PreviewPane;\nimport javafx.scene.Node;\n\npublic class HelloPreviewPane extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Preview Pane\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Preview of the page to be printed\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return PreviewPane.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        CalendarSource workCalendarSource = new CalendarSource(\"Work\");\n        workCalendarSource.getCalendars().addAll(meetings, training, customers, holidays);\n\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, katja, dirk, philip, jule, armin);\n\n        PreviewPane printPreview = new PreviewPane();\n        printPreview.getPrintablePage().getCalendarSources().addAll(workCalendarSource, familyCalendarSource);\n\n        return printPreview;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloPrintView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.print.PrintView;\nimport javafx.scene.Node;\n\npublic class HelloPrintView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Print View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The view for printing that combines all the other print controls and allows the user to print one or more days, one or more weeks, or one or more months.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return PrintView.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        CalendarSource workCalendarSource = new CalendarSource(\"Work\");\n        workCalendarSource.getCalendars().addAll(meetings, training, customers, holidays);\n\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, katja, dirk, philip);\n\n        Entry<String> meetings1 = new Entry<>(\"Meetings 1\");\n        meetings1.setCalendar(meetings);\n\n        PrintView printView = new PrintView();\n        printView.setPrefWidth(1200);\n        printView.setPrefHeight(950);\n        printView.getCalendarSources().addAll(workCalendarSource, familyCalendarSource);\n\n        return printView;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloSettingsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.print.SettingsView;\nimport javafx.scene.Node;\n\npublic class HelloSettingsView extends CalendarFXSample {\n\n    @Override\n    protected Node createControl() {\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        CalendarSource workCalendarSource = new CalendarSource(\"Work\");\n        workCalendarSource.getCalendars().addAll(meetings, training, customers, holidays);\n\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, katja, dirk, philip);\n\n        SettingsView printSettingsView = new SettingsView();\n        printSettingsView.getSourceView().getCalendarSources().addAll(workCalendarSource, familyCalendarSource);\n\n        return printSettingsView;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return SettingsView.class;\n    }\n\n    @Override\n    public String getSampleName() {\n        return \"Print Settings View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Print Settings represents the right panel on the Print dialog that allows to setup the printing.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloTimeRangeField.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.print.TimeRangeField;\nimport javafx.scene.Node;\n\npublic class HelloTimeRangeField extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Time Range Field\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Allows to setup a field on the time range selector.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return TimeRangeField.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        return new TimeRangeField();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/print/HelloTimeRangeView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.print;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.print.TimeRangeView;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\n\npublic class HelloTimeRangeView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Time Range View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Allows to configure the period to be printed via the print dialog\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return TimeRangeView.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        return new TimeRangeView();\n    }\n\n    @Override\n    protected Node wrap(Node node) {\n        TimeRangeView field = (TimeRangeView) node;\n\n        Label label = new Label(\"Range:   \" + field.getStartDate() + \"    to    \" + field.getEndDate());\n        label.setMaxHeight(Double.MAX_VALUE);\n        field.startDateProperty().addListener(it -> label.setText(\"Range:   \" + field.getStartDate() + \"    to    \" + field.getEndDate()));\n        field.endDateProperty().addListener(it -> label.setText(\"Range:   \" + field.getStartDate() + \"    to    \" + field.getEndDate()));\n\n        VBox box2 = new VBox(20, field, label);\n        box2.setFillWidth(false);\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 20px;\");\n        stackPane.getChildren().add(box2);\n\n        HBox box = new HBox(stackPane);\n        box.setStyle(\"-fx-padding: 100px;\");\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n\n        return box;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloAgendaView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.DateControl;\n\npublic class HelloAgendaView extends CalendarFXDateControlSample {\n\n    private AgendaView agendaView;\n\n    @Override\n    public String getSampleName() {\n        return \"Agenda View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        agendaView = new AgendaView();\n        agendaView.setPrefWidth(400);\n\n        CalendarSource calendarSource = new CalendarSource();\n        HelloCalendar calendar1 = new HelloCalendar();\n        HelloCalendar calendar2 = new HelloCalendar();\n        HelloCalendar calendar3 = new HelloCalendar();\n        HelloCalendar calendar4 = new HelloCalendar();\n        calendarSource.getCalendars().addAll(calendar1, calendar2, calendar3, calendar4);\n\n        agendaView.getCalendarSources().add(calendarSource);\n\n        return agendaView;\n    }\n\n    @Override\n    protected boolean isSupportingDeveloperConsole() {\n        return false;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return AgendaView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The agenda view displays a (text) list of calendar entries for today and several days into the future or past.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloAllDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.DateControl;\nimport javafx.beans.binding.Bindings;\n\nimport java.time.LocalDate;\n\npublic class HelloAllDayView extends CalendarFXDateControlSample {\n\n    private AllDayView allDayView;\n\n    @Override\n    public String getSampleName() {\n        return \"All Day View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloCalendar());\n\n        allDayView = new AllDayView();\n        allDayView.prefWidthProperty().bind(Bindings.multiply(150, allDayView.numberOfDaysProperty()));\n        allDayView.setMaxWidth(Double.MAX_VALUE);\n        allDayView.getCalendarSources().add(calendarSource);\n\n        return allDayView;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return AllDayView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The all-day view displays entries that last all day / span multiple days.\";\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar() {\n            Entry<?> entry1 = new Entry<>(\"Entry 1\");\n            Entry<?> entry2 = new Entry<>(\"Entry 2\");\n            Entry<?> entry3 = new Entry<>(\"Entry 3\");\n            Entry<?> entry4 = new Entry<>(\"Entry 4\");\n            Entry<?> entry5 = new Entry<>(\"Entry 5\");\n\n            entry1.setInterval(LocalDate.now(), LocalDate.now().plusDays(2));\n            entry2.setInterval(LocalDate.now().plusDays(3), LocalDate.now().plusDays(4));\n            entry3.setInterval(LocalDate.now().plusDays(1), LocalDate.now().plusDays(2));\n            entry4.setInterval(LocalDate.now(), LocalDate.now().plusDays(4));\n            entry5.setInterval(LocalDate.now().plusDays(4), LocalDate.now().plusDays(6));\n\n            entry1.setFullDay(true);\n            entry2.setFullDay(true);\n            entry3.setFullDay(true);\n            entry4.setFullDay(true);\n            entry5.setFullDay(true);\n\n            entry1.setCalendar(this);\n            entry4.setCalendar(this);\n            entry3.setCalendar(this);\n            entry2.setCalendar(this);\n            entry5.setCalendar(this);\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloAvailabilityCalendar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase.HoursLayoutStrategy;\nimport com.calendarfx.view.VirtualGrid;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ChoiceBox;\nimport javafx.scene.layout.VBox;\nimport javafx.util.StringConverter;\n\nimport java.time.temporal.ChronoUnit;\n\npublic class HelloAvailabilityCalendar extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Availability Calendar\";\n    }\n\n    protected Node createControl() {\n        dayView.setAvailabilityCalendar(new HelloDayViewCalendar());\n        dayView.setHoursLayoutStrategy(HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n        return dayView;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        CheckBox editMode = new CheckBox(\"Edit Availability\");\n        editMode.selectedProperty().bindBidirectional(dayView.editAvailabilityProperty());\n\n        ChoiceBox<VirtualGrid> gridBox = new ChoiceBox<>();\n        gridBox.setConverter(new StringConverter<>() {\n            @Override\n            public String toString(VirtualGrid virtualGrid) {\n                return virtualGrid.getName();\n            }\n\n            @Override\n            public VirtualGrid fromString(String s) {\n                return null;\n            }\n        });\n        gridBox.getItems().add(dayView.getAvailabilityGrid());\n        gridBox.getItems().add(new VirtualGrid(\"10 Minutes\", \"10m\", ChronoUnit.MINUTES, 10));\n        gridBox.getItems().add(new VirtualGrid(\"15 Minutes\", \"15m\", ChronoUnit.MINUTES, 15));\n        gridBox.getItems().add(new VirtualGrid(\"1 Hour\", \"1h\", ChronoUnit.HOURS, 1));\n        gridBox.valueProperty().bindBidirectional(dayView.availabilityGridProperty());\n\n        VBox box = new VBox(10, editMode, gridBox);\n        return box;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The availability calendar can be used to visualize in a read-only way when a person or a resource is available or not.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar() {\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloCalendar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.YearMonth;\n\n/**\n * Created by gdiaz on 26/11/2016.\n */\nclass HelloCalendar extends Calendar {\n\n    public HelloCalendar() {\n        for (int i = 1; i < 28; i++) {\n\n            LocalDate date = YearMonth.now().atDay(i);\n\n            for (int j = 0; j < (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(date);\n                entry.changeEndDate(date.plusDays((int) (Math.random() * 4)));\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.min(24 - hour,\n                        (int) (Math.random() * 4));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                if (Math.random() < .3) {\n                    entry.setFullDay(true);\n                }\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloCalendarHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.view.CalendarHeaderView;\nimport javafx.scene.Node;\n\npublic class HelloCalendarHeaderView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Calendar Header View\";\n    }\n\n    @Override\n    protected Node createControl() {\n        CalendarHeaderView calendarHeaderView = new CalendarHeaderView();\n        calendarHeaderView.setNumberOfDays(5);\n        calendarHeaderView.setMaxHeight(30);\n\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        dirk.setStyle(Style.STYLE1);\n        katja.setStyle(Style.STYLE1);\n        philip.setStyle(Style.STYLE2);\n        jule.setStyle(Style.STYLE1);\n        armin.setStyle(Style.STYLE3);\n\n        calendarHeaderView.getCalendars().add(dirk);\n        calendarHeaderView.getCalendars().add(katja);\n        calendarHeaderView.getCalendars().add(philip);\n        calendarHeaderView.getCalendars().add(jule);\n        calendarHeaderView.getCalendars().add(armin);\n\n        return calendarHeaderView;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return CalendarHeaderView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The all-day view displays entries that last all day / span multiple days.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloCalendarSelector.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.view.CalendarSelector;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\n\npublic class HelloCalendarSelector extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Calendar Selector\";\n    }\n\n    @Override\n    protected Node createControl() {\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        CalendarSelector view = new CalendarSelector();\n        view.getCalendars().addAll(meetings, training, customers, holidays);\n        view.setCalendar(meetings);\n\n        Label label = new Label(\"Selected: \" + view.getCalendar().getName());\n        label.setMaxHeight(Double.MAX_VALUE);\n        view.calendarProperty().addListener(it -> label.setText(\"Selected: \" + view.getCalendar().getName()));\n\n        HBox box = new HBox(20);\n        box.setFillHeight(true);\n        box.getChildren().addAll(view, label);\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return CalendarSelector.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A view used to select a calendar from a list of calendars.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloCalendarView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.CalendarView.Page;\nimport com.calendarfx.view.DateControl;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Month;\nimport java.time.YearMonth;\n\npublic class HelloCalendarView extends CalendarFXDateControlSample {\n\n    private CalendarView calendarView;\n\n    @Override\n    public String getSampleName() {\n        return \"Calendar View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource(\"My Calendars\");\n        calendarSource.getCalendars().add(new HelloCalendar());\n\n        calendarView = new CalendarView(Page.values());\n        calendarView.getCalendarSources().add(calendarSource);\n\n        return calendarView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The calendar view displays a single day, a week, a month, and a year.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return CalendarView.class;\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar() {\n            for (Month month : Month.values()) {\n\n                YearMonth yearMonth = YearMonth.of(LocalDate.now().getYear(), month);\n\n                for (int i = 1; i < 28; i++) {\n\n                    LocalDate date = yearMonth.atDay(i);\n\n                    for (int j = 0; j < (int) (Math.random() * 7); j++) {\n                        Entry<?> entry = new Entry<>();\n                        entry.changeStartDate(date);\n                        entry.changeEndDate(date);\n\n                        entry.setTitle(\"Entry \" + (j + 1));\n\n                        int hour = (int) (Math.random() * 23);\n                        int durationInHours = Math.min(24 - hour,\n                                (int) (Math.random() * 4));\n\n                        LocalTime startTime = LocalTime.of(hour, 0);\n                        LocalTime endTime = startTime\n                                .plusHours(durationInHours);\n\n                        entry.changeStartTime(startTime);\n                        entry.changeEndTime(endTime);\n\n                        if (Math.random() < .3) {\n                            entry.setFullDay(true);\n                        }\n\n                        entry.setCalendar(this);\n                    }\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloDayView extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Day View\";\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(dayView.getPropertySheetItems());\n    }\n\n    protected Node createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloDayViewCalendar());\n        dayView.getCalendarSources().setAll(calendarSource);\n        dayView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n        return dayView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day view displays a single day.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar() {\n            createEntries(LocalDate.now());\n        }\n\n        private void createEntries(LocalDate startDate) {\n            for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (Math.random() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloDetailedDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DetailedDayView;\nimport javafx.application.Platform;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloDetailedDayView extends CalendarFXDateControlSample {\n\n    private DetailedDayView dayView;\n\n    @Override\n    public String getSampleName() {\n        return \"Detailed Day View\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The detailed day view aggregates a day view, an all day view, a calendar header (for swimlane layout), and a time scale.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DetailedDayView.class;\n    }\n\n    @Override\n    protected DateControl createControl() {\n        dayView = new DetailedDayView();\n\n        CalendarSource calendarSource = new CalendarSource();\n        dayView.getCalendarSources().setAll(calendarSource);\n\n        HelloCalendar calendar1 = new HelloCalendar();\n        HelloCalendar calendar2 = new HelloCalendar();\n        HelloCalendar calendar3 = new HelloCalendar();\n        HelloCalendar calendar4 = new HelloCalendar();\n\n        calendar1.setName(\"Calendar 1\");\n        calendar1.setShortName(\"C1\");\n        calendar2.setName(\"Calendar 2\");\n        calendar2.setShortName(\"C2\");\n        calendar3.setName(\"Calendar 3\");\n        calendar3.setShortName(\"C3\");\n        calendar4.setName(\"Calendar 4\");\n        calendar4.setShortName(\"C4\");\n\n        calendar1.setStyle(Calendar.Style.STYLE1);\n        calendar2.setStyle(Calendar.Style.STYLE2);\n        calendar3.setStyle(Calendar.Style.STYLE3);\n        calendar4.setStyle(Calendar.Style.STYLE4);\n\n        calendarSource.getCalendars().setAll(calendar1, calendar2, calendar3, calendar4);\n\n        Platform.runLater(() -> {\n            calendar1.createData();\n            calendar2.createData();\n            calendar3.createData();\n            calendar4.createData();\n        });\n\n        return dayView;\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar() {\n        }\n\n        public void createData() {\n            LocalDate date = LocalDate.now();\n\n            for (int i = 1; i < 3; i++) {\n\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(date);\n                entry.changeEndDate(date);\n\n                entry.setTitle(\"Entry \" + i);\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.min(24 - hour, (int) (Math.random() * 4));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                if (Math.random() < .1) {\n                    entry.setFullDay(true);\n                    entry.setTitle(\"Full Day Entry\");\n                }\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloDetailedWeekView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DetailedWeekView;\n\npublic class HelloDetailedWeekView extends CalendarFXDateControlSample {\n\n    private DetailedWeekView detailedWeekView;\n\n    @Override\n    public String getSampleName() {\n        return \"Detailed Week View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        dirk.setStyle(Style.STYLE1);\n        katja.setStyle(Style.STYLE2);\n        philip.setStyle(Style.STYLE3);\n        jule.setStyle(Style.STYLE4);\n        armin.setStyle(Style.STYLE5);\n\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().setAll(dirk, katja, philip, jule, armin);\n\n        detailedWeekView = new DetailedWeekView();\n        detailedWeekView.getCalendarSources().setAll(calendarSource);\n\n        return detailedWeekView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The detailed week view displays several days inside a week view. \" +\n                \"Additionally it shows an all day view at the top and a time scale \" +\n                \"on its left-hand side\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DetailedWeekView.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloMonthSheetView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.MonthSheetView;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\n\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloMonthSheetView extends CalendarFXDateControlSample {\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n    private MonthSheetView monthView;\n\n    @Override\n    public String getSampleName() {\n        return \"Month Sheet View\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return MonthSheetView.class;\n    }\n\n    private enum CellType {\n        STANDARD,\n        USAGE,\n        DETAILED,\n        BADGE\n    }\n\n    @Override\n    public Node getControlPanel() {\n        VBox box = new VBox();\n\n        ComboBox<CellType> comboBox = new ComboBox<>();\n        comboBox.getItems().addAll(CellType.values());\n        comboBox.setValue(CellType.STANDARD);\n        comboBox.valueProperty().addListener(it -> {\n            switch (comboBox.getValue()) {\n                case USAGE:\n                    monthView.setCellFactory(param -> new MonthSheetView.UsageDateCell(param.getView(), param.getDate()));\n                    break;\n                case BADGE:\n                    monthView.setCellFactory(param -> new MonthSheetView.BadgeDateCell(param.getView(), param.getDate()));\n                    break;\n                case DETAILED:\n                    monthView.setCellFactory(param -> new MonthSheetView.DetailedDateCell(param.getView(), param.getDate()));\n                    break;\n                case STANDARD:\n                    monthView.setCellFactory(param -> new MonthSheetView.SimpleDateCell(param.getView(), param.getDate()));\n                    break;\n            }\n        });\n\n        box.getChildren().add(comboBox);\n        final CalendarPropertySheet propertySheet = new CalendarPropertySheet(monthView.getPropertySheetItems());\n        VBox.setVgrow(propertySheet, Priority.ALWAYS);\n        box.getChildren().add(propertySheet);\n\n        return box;\n    }\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource source = new CalendarSource();\n        source.setName(\"Demo Source\");\n\n        Calendar[] calendar = new Calendar[7];\n        for (int i = 0; i < 7; i++) {\n            calendar[i] = new Calendar(\"Calendar \" + i);\n            calendar[i].setStyle(Calendar.Style.getStyle(i));\n        }\n\n        for (int i = 0; i < 1000; i++) {\n            Entry<String> entry = new Entry<>(\"Entry \" + i);\n            LocalDate date = LocalDate.now();\n            if (Math.random() < .5) {\n                date = date.minusDays((long) (Math.random() * 365));\n            } else {\n                date = date.plusDays((long) (Math.random() * 365));\n            }\n\n            LocalTime start = LocalTime.of((int) (Math.random() * 20), (int) (Math.random() * 30));\n            Duration duration = Duration.ofHours((int) (Math.random() * 8));\n            LocalTime end = start.plus(duration);\n            if (end.isBefore(start)) {\n                end = LocalTime.MAX;\n            }\n\n            entry.changeStartDate(date);\n            entry.changeEndDate(date);\n            entry.changeStartTime(start);\n            entry.changeEndTime(end);\n\n            if (Math.random() > .9) {\n                entry.setFullDay(true);\n            }\n\n            entry.setCalendar(calendar[(int) (Math.random() * 7)]);\n        }\n\n        source.getCalendars().addAll(calendar);\n\n        monthView = new MonthSheetView();\n        monthView.getCalendarSources().add(source);\n        monthView.setCellFactory(param -> new MonthSheetView.DetailedDateCell(param.getView(), param.getDate()));\n\n        return monthView;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloMonthView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.MonthView;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.YearMonth;\n\npublic class HelloMonthView extends CalendarFXDateControlSample {\n\n    private MonthView monthView;\n\n    @Override\n    public String getSampleName() {\n        return \"Month View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        monthView = new MonthView();\n\n        CalendarSource calendarSource = new CalendarSource();\n        HelloCalendar calendar1 = new HelloCalendar(monthView.getYearMonth());\n        HelloCalendar calendar2 = new HelloCalendar(monthView.getYearMonth());\n        HelloCalendar calendar3 = new HelloCalendar(monthView.getYearMonth());\n        HelloCalendar calendar4 = new HelloCalendar(monthView.getYearMonth());\n\n        calendar1.setName(\"Calendar 1\");\n        calendar2.setName(\"Calendar 2\");\n        calendar3.setName(\"Calendar 3\");\n        calendar4.setName(\"Calendar 4\");\n\n        calendar1.setStyle(Style.STYLE1);\n        calendar2.setStyle(Style.STYLE2);\n        calendar3.setStyle(Style.STYLE3);\n        calendar4.setStyle(Style.STYLE4);\n\n        calendarSource.getCalendars().setAll(calendar1, calendar2, calendar3, calendar4);\n\n        monthView.getCalendarSources().setAll(calendarSource);\n\n        return monthView;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return MonthView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The month view displays the month of a given date.\";\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar(YearMonth month) {\n            createEntries(month);\n        }\n\n        private void createEntries(YearMonth month) {\n            for (int i = 1; i < 28; i++) {\n\n                LocalDate date = month.atDay(i);\n\n                for (int j = 0; j < (int) (Math.random() * 2); j++) {\n                    Entry<?> entry = new Entry<>();\n\n                    entry.setTitle(\"Entry \" + (j + 1));\n\n                    LocalDate startDate = date;\n                    LocalDate endDate = startDate.plusDays((int) (Math.random() * 4));\n\n                    int hour = (int) (Math.random() * 23);\n                    int durationInHours = Math.min(23 - hour, (int) (Math.random() * 4));\n\n                    LocalTime startTime = LocalTime.of(hour, 0);\n                    LocalTime endTime = startTime.plusHours(durationInHours);\n\n                    entry.setInterval(startDate, startTime, endDate, endTime);\n\n                    if (Math.random() < .3) {\n                        entry.setFullDay(true);\n                    }\n\n                    entry.setCalendar(this);\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloRecurrenceView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.RecurrenceView;\nimport javafx.geometry.Orientation;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Separator;\nimport javafx.scene.layout.VBox;\n\npublic class HelloRecurrenceView extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Recurrence View\";\n    }\n\n    @Override\n    protected Node createControl() {\n        RecurrenceView view = new RecurrenceView();\n\n        Label label = new Label(\"Rule: \" + view.getRecurrenceRule());\n        label.setMaxWidth(300);\n        label.setWrapText(true);\n\n        view.recurrenceRuleProperty().addListener(it -> label.setText(view.getRecurrenceRule()));\n\n        Separator separator = new Separator(Orientation.HORIZONTAL);\n\n        VBox box = new VBox(20);\n        box.setFillWidth(true);\n        box.getChildren().addAll(view, separator, label);\n        box.setAlignment(Pos.CENTER);\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return RecurrenceView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The recurrence view allows the user to specify recurrence rules according to RFC 2445.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloResourcesCalendarView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Marker;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase.OverlapResolutionStrategy;\nimport com.calendarfx.view.ResourceCalendarView;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\n\npublic class HelloResourcesCalendarView extends CalendarFXSample {\n\n    private final ResourceCalendarView<String> view = new ResourceCalendarView<>();\n\n    @Override\n    public String getSampleName() {\n        return \"Resources Calendar View\";\n    }\n\n    @Override\n    public Node createControl() {\n        view.setShowNoonMarker(false);\n        view.setOverlapResolutionStrategy(OverlapResolutionStrategy.VISUAL_BOUNDS);\n        view.setHeaderFactory(resource -> {\n            Label label1 = new Label(\"IG-TR\");\n            Label label2 = new Label(\"29.000\");\n            Label label3 = new Label(\"13.200\");\n            Label label4 = new Label(\"92 (CPC)\");\n\n            label1.setStyle(\"-fx-font-family: Monospaced; -fx-font-style: bold;\");\n            label2.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n            label3.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n            label4.setStyle(\"-fx-font-family: Monospaced; -fx-text-fill: blue; -fx-font-style: bold;\");\n\n            label1.setAlignment(Pos.CENTER);\n            label2.setAlignment(Pos.CENTER);\n            label3.setAlignment(Pos.CENTER);\n            label4.setAlignment(Pos.CENTER);\n\n            VBox box = new VBox(5, label1, label2, label3, label4);\n            box.setFillWidth(true);\n\n            if (resource.equals(\"Resource 1\")) {\n                box.setPrefWidth(400);\n            }\n\n            return box;\n        });\n\n        for (int i = 0; i < 5; i++) {\n            CalendarSource source = new CalendarSource(\"Default\");\n\n            Calendar calendar1 = new HelloDayViewCalendar(\"cal1\");\n            calendar1.setStyle(Style.STYLE1);\n            source.getCalendars().add(calendar1);\n\n            Calendar calendar2 = new HelloDayViewCalendar(\"cal2\");\n            calendar2.setStyle(Style.STYLE2);\n            source.getCalendars().add(calendar2);\n\n            Calendar calendar3 = new HelloDayViewCalendar(\"cal3\");\n            calendar3.setStyle(Style.STYLE3);\n            source.getCalendars().add(calendar3);\n\n            String resource = \"Resource \" + (i + 1);\n            view.getResources().add(resource);\n\n            DayView dayView = view.getDayView(resource);\n            dayView.setEnableCurrentTimeMarker(true);\n            dayView.setEnableCurrentTimeCircle(i == 0);\n            dayView.getCalendarSources().setAll(source);\n            dayView.setEntryViewFactory(entry -> new DayEntryView(entry) {\n                {\n                    setPrefHeight(25);\n//                    setHeightLayoutStrategy(HeightLayoutStrategy.COMPUTE_PREF_SIZE);\n                }\n            });\n\n            if (i % 2 == 1) {\n                dayView.setStyle(\"-fx-background-color: #e9e9e9;\");\n            }\n        }\n\n        Marker marker1 = new Marker();\n        marker1.setTitle(\"My Marker 1\");\n        marker1.setTime(ZonedDateTime.now().minusHours(1));\n        view.getMarkers().add(marker1);\n\n        Marker marker2 = new Marker();\n        marker2.setTitle(\"My Marker 2\");\n        marker2.setTime(ZonedDateTime.now().plusHours(1));\n        marker2.getStyleClass().add(\"marker2\");\n        view.getMarkers().add(marker2);\n\n        view.setPrefHeight(800);\n\n        return view;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        CheckBox editSchedule = new CheckBox(\"Edit Schedule\");\n        editSchedule.selectedProperty().bindBidirectional(view.editAvailabilityProperty());\n        return editSchedule;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The resource calendar view can scroll vertically up and down with infinite scrolling enabled.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar(String name) {\n            setName(name);\n            createEntries(LocalDate.now().minusDays(2));\n            createEntries(LocalDate.now().minusDays(1));\n            createEntries(LocalDate.now());\n            createEntries(LocalDate.now().plusDays(1));\n            createEntries(LocalDate.now().plusDays(2));\n        }\n\n        private void createEntries(LocalDate startDate) {\n            for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (Math.random() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloScrollingDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloScrollingDayView extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Day View (Scrolling)\";\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(dayView.getPropertySheetItems());\n    }\n\n    protected Node createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloDayViewCalendar());\n        dayView.getCalendarSources().setAll(calendarSource);\n        dayView.setScrollingEnabled(true);\n        dayView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n        dayView.setPrefHeight(500);\n\n        return dayView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day view can scroll vertically up and down with infinite scrolling enabled.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar() {\n            createEntries(LocalDate.now());\n        }\n\n        private void createEntries(LocalDate startDate) {\n            for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (Math.random() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloScrollingTimeScaleView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.TimeScaleView;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.effect.Reflection;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDateTime;\n\npublic class HelloScrollingTimeScaleView extends CalendarFXSample {\n\n    public static final String STYLE_LABEL_TIME_EVEN_HOURS = \"-fx-text-fill: gray;\";\n    public static final String STYLE_LABEL_TIME_ODD_HOURS = \"-fx-text-fill: darkblue;\";\n    public static final String STYLE_LABEL_DATE_SUNDAY = \"-fx-background-color: red;\";\n\n    @Override\n    public String getSampleName() {\n        return \"Time Scale (Scrolling)\";\n    }\n\n    @Override\n    protected Node createControl() {\n        return null;\n    }\n\n    @Override\n    public Node getPanel(Stage stage) {\n        TimeScaleView view = new TimeScaleView();\n        view.setTimeStyleProvider(this::provideTimeStyle);\n        view.setDateStyleProvider(this::provideDateStyle);\n        view.setScrollingEnabled(true);\n\n        return wrap(view);\n    }\n\n    @Override\n    public Node wrap(Node node) {\n\n        HBox box = new HBox();\n        box.setStyle(\"-fx-padding: 100px;\");\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\n                \"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 0 20 0 20;\");\n        box.getChildren().add(stackPane);\n\n        stackPane.getChildren().add(node);\n        stackPane.setEffect(new Reflection());\n\n        stackPane.setPrefHeight(2000);\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return TimeScaleView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The scale shows the time of day vertically.\";\n    }\n\n    private String provideTimeStyle(LocalDateTime dateTime) {\n        return dateTime.getHour() % 2 == 0 ? STYLE_LABEL_TIME_EVEN_HOURS : STYLE_LABEL_TIME_ODD_HOURS;\n    }\n\n    private String provideDateStyle(LocalDateTime dateTime) {\n        return dateTime.getDayOfWeek() == DayOfWeek.SUNDAY ? STYLE_LABEL_DATE_SUNDAY : null;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloSourceGridView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.SourceGridView;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.layout.VBox;\n\npublic class HelloSourceGridView extends CalendarFXSample {\n\n    private SourceGridView sourceView;\n\n    @Override\n    public String getSampleName() {\n        return \"Source Grid View\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return SourceGridView.class;\n    }\n\n    @Override\n    protected Node createControl() {\n        sourceView = new SourceGridView();\n\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        CalendarSource workCalendarSource = new CalendarSource(\"Work\");\n        workCalendarSource.getCalendars().addAll(meetings, training, customers, holidays);\n\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, katja, dirk, philip, jule, armin);\n\n        sourceView.getCalendarSources().addAll(workCalendarSource, familyCalendarSource);\n\n        return sourceView;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        Node controlPanel = super.getControlPanel();\n\n        VBox vBox = new VBox();\n        vBox.setSpacing(5);\n        vBox.getChildren().add(controlPanel);\n\n        for (CalendarSource calendarSource : sourceView.getCalendarSources()) {\n            for (Calendar calendar : calendarSource.getCalendars()) {\n                CheckBox checkBox = new CheckBox(calendar.getName());\n                checkBox.selectedProperty().bindBidirectional(sourceView.getCalendarVisibilityProperty(calendar));\n                vBox.getChildren().add(checkBox);\n            }\n        }\n\n        return vBox;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloSourceView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.SourceView;\nimport javafx.geometry.Orientation;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Separator;\nimport javafx.scene.layout.VBox;\n\npublic class HelloSourceView extends CalendarFXSample {\n\n    private SourceView sourceView;\n\n    private CalendarSource workCalendarSource;\n\n    private CalendarSource familyCalendarSource;\n\n    @Override\n    public String getSampleName() {\n        return \"Source View\";\n    }\n\n    @Override\n    protected Node createControl() {\n        sourceView = new SourceView();\n\n        Calendar meetings = new Calendar(\"Meetings\");\n        Calendar training = new Calendar(\"Training\");\n        Calendar customers = new Calendar(\"Customers\");\n        Calendar holidays = new Calendar(\"Holidays\");\n\n        meetings.setStyle(Style.STYLE2);\n        training.setStyle(Style.STYLE3);\n        customers.setStyle(Style.STYLE4);\n        holidays.setStyle(Style.STYLE5);\n\n        workCalendarSource = new CalendarSource(\"Work\");\n        workCalendarSource.getCalendars().addAll(meetings, training, customers,\n                holidays);\n\n        Calendar birthdays = new Calendar(\"Birthdays\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(birthdays, katja, dirk,\n                philip, jule, armin);\n\n        sourceView.getCalendarSources().addAll(workCalendarSource,\n                familyCalendarSource);\n\n        return sourceView;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        VBox box = new VBox();\n        box.setSpacing(5);\n        box.setFillWidth(true);\n\n        Button addWorkCalendar = new Button(\"Add Work Calendar\");\n        addWorkCalendar.setOnAction(evt -> addWorkCalendar());\n\n        Button removeWorkCalendar = new Button(\"Remove Work Calendar\");\n        removeWorkCalendar.setOnAction(evt -> removeWorkCalendar());\n\n        Button addWorkCalendarSource = new Button(\"Add Work Calendar Source\");\n        addWorkCalendarSource.setOnAction(evt -> addWorkCalendarSource());\n\n        Button removeWorkCalendarSource = new Button(\n                \"Remove Work Calendar Source\");\n        removeWorkCalendarSource.setOnAction(evt -> removeWorkCalendarSource());\n\n        box.getChildren().addAll(addWorkCalendar, removeWorkCalendar,\n                addWorkCalendarSource, removeWorkCalendarSource);\n\n        Button addFamilyCalendar = new Button(\"Add Family Calendar\");\n        addFamilyCalendar.setOnAction(evt -> addFamilyCalendar());\n\n        Button removeFamilyCalendar = new Button(\"Remove Family Calendar\");\n        removeFamilyCalendar.setOnAction(evt -> removeFamilyCalendar());\n\n        Button addFamilyCalendarSource = new Button(\n                \"Add Family Calendar Source\");\n        addFamilyCalendarSource.setOnAction(evt -> addFamilyCalendarSource());\n\n        Button removeFamilyCalendarSource = new Button(\n                \"Remove Family Calendar Source\");\n        removeFamilyCalendarSource\n                .setOnAction(evt -> removeFamilyCalendarSource());\n\n        box.getChildren().addAll(new Separator(Orientation.HORIZONTAL),\n                addFamilyCalendar, removeFamilyCalendar,\n                addFamilyCalendarSource, removeFamilyCalendarSource);\n\n        addWorkCalendar.setMaxWidth(Double.MAX_VALUE);\n        removeWorkCalendar.setMaxWidth(Double.MAX_VALUE);\n        addWorkCalendarSource.setMaxWidth(Double.MAX_VALUE);\n        removeWorkCalendarSource.setMaxWidth(Double.MAX_VALUE);\n\n        addFamilyCalendar.setMaxWidth(Double.MAX_VALUE);\n        removeFamilyCalendar.setMaxWidth(Double.MAX_VALUE);\n        addFamilyCalendarSource.setMaxWidth(Double.MAX_VALUE);\n        removeFamilyCalendarSource.setMaxWidth(Double.MAX_VALUE);\n\n        return box;\n    }\n\n    private int calendarCounter = 1;\n\n    private void addWorkCalendar() {\n        Calendar calendar = new Calendar(\"Work Calendar \" + calendarCounter++);\n        calendar.setStyle(Style.getStyle(calendarCounter));\n        workCalendarSource.getCalendars().add(calendar);\n    }\n\n    private void removeWorkCalendar() {\n        workCalendarSource.getCalendars().remove(\n                workCalendarSource.getCalendars().size() - 1);\n    }\n\n    private void addWorkCalendarSource() {\n        sourceView.getCalendarSources().add(workCalendarSource);\n    }\n\n    private void removeWorkCalendarSource() {\n        sourceView.getCalendarSources().remove(workCalendarSource);\n    }\n\n    private void addFamilyCalendar() {\n        Calendar calendar = new Calendar(\"Family Calendar \" + calendarCounter++);\n        calendar.setStyle(Style.getStyle(calendarCounter));\n        familyCalendarSource.getCalendars().add(calendar);\n    }\n\n    private void addFamilyCalendarSource() {\n        sourceView.getCalendarSources().add(familyCalendarSource);\n    }\n\n    private void removeFamilyCalendar() {\n        familyCalendarSource.getCalendars().remove(\n                familyCalendarSource.getCalendars().size() - 1);\n    }\n\n    private void removeFamilyCalendarSource() {\n        sourceView.getCalendarSources().remove(familyCalendarSource);\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return SourceView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Shows all calendar sources. Sources are used to group calendars together that all origin from the same \"\n                + \"source, e.g. Google calendar. Sources can be collapsed by clicking on their name.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloTimeField.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.TimeField;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class HelloTimeField extends CalendarFXSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Time Field\";\n    }\n\n    @Override\n    protected Node createControl() {\n        TimeField timeField = new TimeField();\n        Label label = new Label(\"Time: \");\n        label.setText(\"Time: \" + timeField.getValue().toString());\n        timeField.valueProperty().addListener(it -> label.setText(\"Time: \" + timeField.getValue().toString()));\n\n        VBox box = new VBox();\n        box.setSpacing(20);\n        box.getChildren().addAll(timeField, label);\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return TimeField.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"A control used to specify a local time (hour, minute).\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloTimeScaleView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.TimeScaleView;\nimport impl.com.calendarfx.view.DayViewScrollPane;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.effect.Reflection;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport java.time.LocalDateTime;\n\npublic class HelloTimeScaleView extends CalendarFXSample {\n\n    public static final String STYLE_LABEL_TIME_EVEN_HOURS = \"-fx-text-fill: gray;\";\n    public static final String STYLE_LABEL_TIME_ODD_HOURS = \"-fx-text-fill: darkblue;\";\n\n    @Override\n    public String getSampleName() {\n        return \"Time Scale\";\n    }\n\n    @Override\n    protected Node createControl() {\n        return null;\n    }\n\n    @Override\n    public Node getPanel(Stage stage) {\n        TimeScaleView view = new TimeScaleView();\n        view.setTimeStyleProvider(this::provideTimeStyle);\n\n        final DayViewScrollPane scrollPane = new DayViewScrollPane(view, new ScrollBar());\n        scrollPane.setPrefHeight(2000);\n        return wrap(scrollPane);\n    }\n\n    @Override\n    public Node wrap(Node node) {\n\n        HBox box = new HBox();\n        box.setStyle(\"-fx-padding: 100px;\");\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 0 20 0 20;\");\n        box.getChildren().add(stackPane);\n\n        stackPane.getChildren().add(node);\n        stackPane.setEffect(new Reflection());\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return TimeScaleView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The scale shows the time of day vertically.\";\n    }\n\n    private String provideTimeStyle(LocalDateTime dateTime) {\n        return dateTime.getHour() % 2 == 0 ? STYLE_LABEL_TIME_EVEN_HOURS : STYLE_LABEL_TIME_ODD_HOURS;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloTimezones.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl.Layout;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.util.List;\nimport java.util.Map;\n\npublic class HelloTimezones extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Time Zones\";\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(dayView.getPropertySheetItems());\n    }\n\n    protected Node createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n\n        ParisCalendar calendar = new ParisCalendar();\n        calendar.setStyle(Style.STYLE1);\n        calendarSource.getCalendars().add(calendar);\n\n        LondonCalendar timeZoneCalendar = new LondonCalendar(calendar);\n        timeZoneCalendar.setStyle(Style.STYLE2);\n        calendarSource.getCalendars().add(timeZoneCalendar);\n\n        dayView.setZoneId(ZoneId.of(\"Europe/Paris\"));\n        dayView.setEarlyLateHoursStrategy(EarlyLateHoursStrategy.SHOW_COMPRESSED);\n        dayView.setStartTime(LocalTime.of(6, 0));\n        dayView.setEndTime(LocalTime.of(20, 0));\n        dayView.setLayout(Layout.SWIMLANE);\n        dayView.getCalendarSources().setAll(calendarSource);\n        dayView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n        return dayView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day view displays a single day.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class LondonCalendar extends Calendar {\n        public LondonCalendar(ParisCalendar input) {\n            Instant earliestTimeUsed = input.getEarliestTimeUsed();\n            Instant latestTimeUsed = input.getLatestTimeUsed();\n            Map<LocalDate, List<Entry<?>>> entries = input.findEntries(LocalDate.ofInstant(earliestTimeUsed, ZoneId.systemDefault()), LocalDate.ofInstant(latestTimeUsed, ZoneId.systemDefault()), ZoneId.systemDefault());\n            entries.values().forEach(entryList -> {\n                entryList.forEach(e -> {\n                    Entry<?> copiedEntry = new Entry<>();\n                    copiedEntry.setTitle(e.getTitle().replace(\"Paris\", \"Chicago\"));\n                    copiedEntry.setInterval(e.getStartDate(), e.getStartTime(), e.getEndDate(), e.getEndTime(), ZoneId.of(\"America/Chicago\"));\n                    addEntries(copiedEntry);\n                });\n            });\n        }\n    }\n\n    class ParisCalendar extends Calendar {\n\n        public ParisCalendar() {\n            LocalDate date = LocalDate.now();\n\n            ZoneId zoneId = ZoneId.of(\"Europe/Paris\");\n\n            Entry<?> entry = new Entry<>();\n            entry.setTitle(\"Paris\");\n\n            int hour = 2;\n            int durationInHours = 4;\n\n            LocalTime startTime = LocalTime.of(hour, 0);\n            LocalTime endTime = startTime.plusHours(durationInHours);\n\n            entry.setInterval(date, startTime, date, endTime, zoneId);\n\n            addEntry(entry);\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloTopLayer.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.EntryViewBase.AlignmentStrategy;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.util.List;\n\npublic class HelloTopLayer extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Day View with Top Layer\";\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(dayView.getPropertySheetItems());\n    }\n\n    protected Node createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new BaseLayerCalendar());\n        calendarSource.getCalendars().add(new TopLayerCalendar());\n        dayView.getCalendarSources().setAll(calendarSource);\n        dayView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n\n        dayView.visibleLayersProperty().addAll(List.of(DateControl.Layer.BASE, DateControl.Layer.TOP));\n        dayView.setEntryViewFactory(this::createEntryView);\n\n        return dayView;\n    }\n\n    private DayEntryView createEntryView(Entry<?> entry) {\n        DayEntryView entryView = new DayEntryView(entry);\n        if (entry.getCalendar() instanceof TopLayerCalendar) {\n            entryView.setLayer(DateControl.Layer.TOP);\n            entryView.setWidthPercentage(20.0);\n            entryView.setAlignmentStrategy(AlignmentStrategy.ALIGN_RIGHT);\n        }\n        return entryView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day view supports base and top layers functionality.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    static class BaseLayerCalendar extends Calendar {\n\n        public BaseLayerCalendar() {\n            createCalendarEntries(this, \"Entry\", LocalDate.now());\n            setStyle(Style.STYLE1);\n        }\n\n    }\n\n    static class TopLayerCalendar extends Calendar {\n\n        public TopLayerCalendar() {\n            createCalendarEntries(this, \"Note\", LocalDate.now());\n            setStyle(Style.STYLE2);\n        }\n\n    }\n\n    private static void createCalendarEntries(Calendar calendar, String titlePrefix, LocalDate startDate) {\n        for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n            Entry<?> entry = new Entry<>();\n            entry.changeStartDate(startDate);\n            entry.changeEndDate(startDate);\n\n            entry.setTitle(titlePrefix + \" \" + (j + 1));\n\n            int hour = (int) (Math.random() * 23);\n            int durationInHours = Math.max(1, Math.min(24 - hour, (int) (Math.random() * 4)));\n\n            LocalTime startTime = LocalTime.of(hour, 0);\n            LocalTime endTime = startTime.plusHours(durationInHours);\n\n            entry.changeStartTime(startTime);\n            entry.changeEndTime(endTime);\n\n            entry.setCalendar(calendar);\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloVisualBounds.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.DayViewBase.OverlapResolutionStrategy;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport javafx.scene.Node;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloVisualBounds extends CalendarFXSample {\n\n    private final DayView dayView = new DayView();\n\n    @Override\n    public String getSampleName() {\n        return \"Day View (Visual Bounds)\";\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(dayView.getPropertySheetItems());\n    }\n\n    protected Node createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloDayViewCalendar());\n        dayView.setOverlapResolutionStrategy(OverlapResolutionStrategy.VISUAL_BOUNDS);\n        dayView.getCalendarSources().setAll(calendarSource);\n        dayView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n        dayView.setHourHeight(20);\n        dayView.setPrefWidth(200);\n        dayView.setEntryViewFactory(entry -> new DayEntryView(entry) {\n            {\n                setPrefHeight(50);\n                setHeightLayoutStrategy(HeightLayoutStrategy.COMPUTE_PREF_SIZE);\n            }\n        });\n        return dayView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"Entries are considered 'overlapping' if their visual bounds overlap, not when their time bounds do so.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar() {\n            createEntries(LocalDate.now());\n        }\n\n        private void createEntries(LocalDate startDate) {\n            for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (Math.random() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloWeekDayHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.WeekDayHeaderView;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.effect.Reflection;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\n\npublic class HelloWeekDayHeaderView extends CalendarFXDateControlSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Week Day Header View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        return new WeekDayHeaderView();\n    }\n\n    @Override\n    protected Node wrap(Node node) {\n        HBox box = new HBox();\n        box.setMaxWidth(Double.MAX_VALUE);\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 20px;\");\n        box.getChildren().add(stackPane);\n\n        stackPane.getChildren().add(node);\n        stackPane.setEffect(new Reflection());\n\n        return box;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The week day header view displays the labels for each week day.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return WeekDayHeaderView.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloWeekDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.WeekDayView;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class HelloWeekDayView extends CalendarFXDateControlSample {\n\n    private WeekDayView weekDayView;\n\n    @Override\n    public String getSampleName() {\n        return \"Week Day View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloDayViewCalendar());\n\n        weekDayView = new WeekDayView();\n        weekDayView.getCalendarSources().add(calendarSource);\n\n        return weekDayView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The day view displays a single day.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return WeekDayView.class;\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        public HelloDayViewCalendar() {\n            createEntries(LocalDate.now());\n        }\n\n        private void createEntries(LocalDate startDate) {\n            for (int j = 0; j < 5 + (int) (Math.random() * 7); j++) {\n                Entry<?> entry = new Entry<>();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                entry.setTitle(\"Entry \" + (j + 1));\n\n                int hour = (int) (Math.random() * 23);\n                int durationInHours = Math.min(24 - hour,\n                        (int) (Math.random() * 4));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloWeekFieldsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.WeekFieldsView;\nimport javafx.scene.control.Control;\n\npublic class HelloWeekFieldsView extends CalendarFXSample {\n\n    private WeekFieldsView view;\n\n    @Override\n    public String getSampleName() {\n        return \"Week Fields View\";\n    }\n\n    @Override\n    protected Control createControl() {\n        view = new WeekFieldsView();\n        return view;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return AgendaView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The week fields view lets the user specify the first day of the week \" +\n                \"(e.g. MONDAY in Germany, SUNDAY in the US) and the minimum number of \" +\n                \"days in the first week of the year.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloWeekTimeScaleView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.WeekTimeScaleView;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.effect.Reflection;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\n\npublic class HelloWeekTimeScaleView extends CalendarFXDateControlSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Week Time Scale\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        return new WeekTimeScaleView();\n    }\n\n    @Override\n    protected Node wrap(Node node) {\n        HBox box = new HBox();\n        box.setStyle(\"-fx-padding: 100px;\");\n        box.setAlignment(Pos.CENTER);\n        box.setFillHeight(false);\n\n        StackPane stackPane = new StackPane();\n        stackPane.setStyle(\n                \"-fx-background-color: white; -fx-border-color: gray; -fx-border-width: .25px; -fx-padding: 0 20 0 20;\");\n        box.getChildren().add(stackPane);\n\n        stackPane.getChildren().add(node);\n        stackPane.setEffect(new Reflection());\n\n        return box;\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return WeekTimeScaleView.class;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The scale shows the time of day vertically.\";\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloWeekView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.WeekView;\nimport impl.com.calendarfx.view.CalendarPropertySheet;\nimport impl.com.calendarfx.view.DayViewScrollPane;\nimport javafx.scene.Node;\nimport javafx.scene.control.ScrollBar;\nimport javafx.stage.Stage;\n\npublic class HelloWeekView extends CalendarFXSample {\n\n    private final WeekView weekView = new WeekView();\n\n    @Override\n    public String getSampleName() {\n        return \"Week View\";\n    }\n\n    @Override\n    protected Node createControl() {\n        return null;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        return new CalendarPropertySheet(weekView.getPropertySheetItems());\n    }\n\n    @Override\n    public Node getPanel(Stage stage) {\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        dirk.setStyle(Style.STYLE1);\n        katja.setStyle(Style.STYLE2);\n        philip.setStyle(Style.STYLE3);\n        jule.setStyle(Style.STYLE4);\n        armin.setStyle(Style.STYLE5);\n\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(dirk);\n        calendarSource.getCalendars().add(katja);\n        calendarSource.getCalendars().add(philip);\n        calendarSource.getCalendars().add(jule);\n        calendarSource.getCalendars().add(armin);\n\n        weekView.getCalendarSources().setAll(calendarSource);\n\n        DayViewScrollPane scroll = new DayViewScrollPane(weekView, new ScrollBar());\n        scroll.setStyle(\"-fx-background-color: white;\");\n        return scroll;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The week view displays several days. Most commonly this view \" +\n                \"will be used to show the 5 work days of a week or the 7 standard \" +\n                \"days.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return DetailedWeekView.class;\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloYearMonthView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.YearMonthView;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.YearMonth;\n\npublic class HelloYearMonthView extends CalendarFXDateControlSample {\n\n    private YearMonthView yearMonthView;\n\n    @Override\n    public String getSampleName() {\n        return \"Year Month View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        yearMonthView = new YearMonthView();\n\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloCalendar(yearMonthView.getYearMonth()));\n\n        yearMonthView.getCalendarSources().add(calendarSource);\n\n        return yearMonthView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The month view displays the month of a given date.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return YearMonthView.class;\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar(YearMonth month) {\n            createEntries(month);\n        }\n\n        private void createEntries(YearMonth month) {\n            for (int i = 1; i < 28; i++) {\n\n                LocalDate date = month.atDay(i);\n\n                for (int j = 0; j < (int) (Math.random() * 7); j++) {\n                    Entry<?> entry = new Entry<>();\n                    entry.changeStartDate(date);\n                    entry.changeEndDate(date);\n\n                    entry.setTitle(\"Entry \" + (j + 1));\n\n                    int hour = (int) (Math.random() * 23);\n                    int durationInHours = Math.min(24 - hour,\n                            (int) (Math.random() * 4));\n\n                    LocalTime startTime = LocalTime.of(hour, 0);\n                    LocalTime endTime = startTime.plusHours(durationInHours);\n\n                    entry.changeStartTime(startTime);\n                    entry.changeEndTime(endTime);\n\n                    if (Math.random() < .3) {\n                        entry.setFullDay(true);\n                    }\n\n                    entry.setCalendar(this);\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/HelloYearView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.YearView;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Year;\n\npublic class HelloYearView extends CalendarFXDateControlSample {\n\n    @Override\n    public String getSampleName() {\n        return \"Year View\";\n    }\n\n    @Override\n    protected DateControl createControl() {\n        YearView yearView = new YearView();\n\n        CalendarSource calendarSource = new CalendarSource();\n        calendarSource.getCalendars().add(new HelloCalendar(yearView.getYear()));\n\n        yearView.getCalendarSources().add(calendarSource);\n\n        return yearView;\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The year view displays the twelve month of a given year.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return YearView.class;\n    }\n\n    class HelloCalendar extends Calendar {\n\n        public HelloCalendar(Year year) {\n            for (int i = 1; i < 365; i++) {\n\n                LocalDate date = year.atDay(i);\n\n                for (int j = 0; j < (int) (Math.random() * 4); j++) {\n                    Entry<?> entry = new Entry<>();\n                    entry.changeStartDate(date);\n                    entry.changeEndDate(date);\n\n                    entry.setTitle(\"Entry \" + (j + 1));\n\n                    int hour = (int) (Math.random() * 23);\n                    int durationInHours = Math.min(24 - hour, (int) (Math.random() * 4));\n\n                    LocalTime startTime = LocalTime.of(hour, 0);\n                    LocalTime endTime = startTime.plusHours(durationInHours);\n\n                    entry.changeStartTime(startTime);\n                    entry.changeEndTime(endTime);\n\n                    if (Math.random() < .3) {\n                        entry.setFullDay(true);\n                    }\n\n                    entry.setCalendar(this);\n                }\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/com/calendarfx/demo/views/resources/HelloResourcesView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *  Copyright (C) 2006 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.demo.views.resources;\n\nimport com.calendarfx.demo.CalendarFXDateControlSample;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Resource;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DateControl.Layout;\nimport com.calendarfx.view.DayViewBase.AvailabilityEditingEntryBehaviour;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport com.calendarfx.view.DayViewBase.GridType;\nimport com.calendarfx.view.ResourcesView;\nimport com.calendarfx.view.ResourcesView.Type;\nimport javafx.application.Platform;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ChoiceBox;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Slider;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.layout.VBox;\nimport javafx.util.StringConverter;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.function.Supplier;\n\npublic class HelloResourcesView extends CalendarFXDateControlSample {\n\n    private ResourcesView resourcesView;\n\n    public static final int DATA_GENERATION_SEED = 11011;\n\n    final Random random = new Random(DATA_GENERATION_SEED);\n\n    @Override\n    public String getSampleName() {\n        return \"ResourcesView\";\n    }\n\n    @Override\n    public String getSampleDescription() {\n        return \"The resources view is used to display allocations of resources, e.g. people working at a hairdresser and their customer appointments.\";\n    }\n\n    @Override\n    protected Class<?> getJavaDocClass() {\n        return ResourcesView.class;\n    }\n\n    @Override\n    protected boolean isSupportingDeveloperConsole() {\n        return false;\n    }\n\n    @Override\n    public Node getControlPanel() {\n        ToggleButton availabilityButton = new ToggleButton(\"Edit Schedule\");\n        availabilityButton.selectedProperty().bindBidirectional(resourcesView.editAvailabilityProperty());\n\n        DatePicker datePicker = new DatePicker();\n        datePicker.valueProperty().bindBidirectional(resourcesView.dateProperty());\n\n        ChoiceBox<Integer> daysBox = new ChoiceBox<>();\n        daysBox.getItems().setAll(1, 2, 3, 4, 5, 7, 10, 14);\n        daysBox.setValue(resourcesView.getNumberOfDays());\n        daysBox.valueProperty().addListener(it -> resourcesView.setNumberOfDays(daysBox.getValue()));\n\n        ChoiceBox<Integer> numberOfResourcesBox = new ChoiceBox<>();\n        numberOfResourcesBox.getItems().setAll(1, 2, 3, 4, 5);\n        numberOfResourcesBox.setValue(resourcesView.getResources().size());\n        numberOfResourcesBox.valueProperty().addListener(it -> resourcesView.getResources().setAll(createResources(numberOfResourcesBox.getValue())));\n\n        Button memoryTestButton = new Button(\"Test Heap\");\n        memoryTestButton.setOnAction(evt -> {\n            Thread thread = new Thread(() -> {\n                Runtime r = Runtime.getRuntime();\n                int counter = 0;\n                while (true) {\n                    counter++;\n                    final int fc = counter;\n\n                    Platform.runLater(() -> {\n                        List<Resource<String>> resources = createResources(5);\n                        resourcesView.getResources().setAll(resources);\n\n                        resources.forEach(resource -> {\n                            CalendarSource source = new CalendarSource(\"Default\");\n\n                            HelloDayViewCalendar calendar1 = new HelloDayViewCalendar(random.nextLong());\n                            calendar1.generateBaseEntries();\n                            calendar1.setStyle(Style.STYLE1);\n                            source.getCalendars().add(calendar1);\n\n                            HelloDayViewCalendar calendar2 = new HelloDayViewCalendar(random.nextLong());\n                            calendar2.generateBaseEntries();\n                            calendar2.setStyle(Style.STYLE2);\n                            source.getCalendars().add(calendar2);\n\n                            HelloDayViewCalendar calendar3 = new HelloDayViewCalendar(random.nextLong());\n                            calendar3.generateBaseEntries();\n                            calendar3.setStyle(Style.STYLE3);\n                            source.getCalendars().add(calendar3);\n\n                            HelloDayViewCalendar calendar4 = new HelloDayViewCalendar(random.nextLong());\n                            calendar4.generateTopEntries();\n                            calendar4.setStyle(Style.STYLE4);\n                            source.getCalendars().add(calendar4);\n\n                            resource.getCalendarSources().setAll(source);\n                        });\n\n                        System.out.println(fc + \": free: \" + (r.freeMemory() / 1_000) + \" kb\");\n                    });\n                    try {\n                        Thread.sleep(100);\n                    } catch (InterruptedException e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n            });\n            thread.setName(\"Memory Test Thread\");\n            thread.setDaemon(true);\n            thread.start();\n        });\n\n        ChoiceBox<Integer> clicksBox = new ChoiceBox<>();\n        clicksBox.getItems().setAll(1, 2, 3);\n        clicksBox.setValue(resourcesView.getCreateEntryClickCount());\n        clicksBox.valueProperty().addListener(it -> resourcesView.setCreateEntryClickCount(clicksBox.getValue()));\n\n        ChoiceBox<AvailabilityEditingEntryBehaviour> behaviourBox = new ChoiceBox<>();\n        behaviourBox.getItems().setAll(AvailabilityEditingEntryBehaviour.values());\n        behaviourBox.valueProperty().bindBidirectional(resourcesView.entryViewAvailabilityEditingBehaviourProperty());\n\n        ChoiceBox<Type> typeBox = new ChoiceBox<>();\n        typeBox.getItems().setAll(Type.values());\n        typeBox.valueProperty().bindBidirectional(resourcesView.typeProperty());\n        typeBox.setConverter(new StringConverter<>() {\n            @Override\n            public String toString(Type object) {\n                if (object != null) {\n                    if (object.equals(Type.RESOURCES_OVER_DATES)) {\n                        return \"Resources over date\";\n                    } else if (object.equals(Type.DATES_OVER_RESOURCES)) {\n                        return \"Date over resources\";\n                    } else {\n                        return \"unknown view type: \" + object.name();\n                    }\n                }\n                return \"\";\n            }\n\n            @Override\n            public Type fromString(String string) {\n                return null;\n            }\n        });\n\n        ChoiceBox<Layout> layoutBox = new ChoiceBox<>();\n        layoutBox.getItems().setAll(Layout.values());\n        layoutBox.valueProperty().bindBidirectional(resourcesView.layoutProperty());\n        layoutBox.setConverter(new StringConverter<>() {\n            @Override\n            public String toString(Layout object) {\n                if (object != null) {\n                    if (object.equals(Layout.SWIMLANE)) {\n                        return \"Swim Lanes\";\n                    } else if (object.equals(Layout.STANDARD)) {\n                        return \"Standard\";\n                    } else {\n                        return \"unknown layout type: \" + object.name();\n                    }\n                }\n                return \"\";\n            }\n\n            @Override\n            public Layout fromString(String string) {\n                return null;\n            }\n        });\n\n        ChoiceBox<GridType> gridTypeBox = new ChoiceBox<>();\n        gridTypeBox.getItems().setAll(GridType.values());\n        gridTypeBox.valueProperty().bindBidirectional(resourcesView.gridTypeProperty());\n\n        CheckBox infiniteScrolling = new CheckBox(\"Infinite scrolling\");\n        infiniteScrolling.selectedProperty().bindBidirectional(resourcesView.scrollingEnabledProperty());\n        infiniteScrolling.setDisable(true);\n\n        CheckBox adjustBox = new CheckBox(\"Adjust first day of week\");\n        adjustBox.selectedProperty().bindBidirectional(resourcesView.adjustToFirstDayOfWeekProperty());\n\n        CheckBox scrollbarBox = new CheckBox(\"Show scrollbar\");\n        scrollbarBox.selectedProperty().bindBidirectional(resourcesView.showScrollBarProperty());\n\n        CheckBox timescaleBox = new CheckBox(\"Show timescale\");\n        timescaleBox.selectedProperty().bindBidirectional(resourcesView.showTimeScaleViewProperty());\n\n        CheckBox allDayBox = new CheckBox(\"Show all day events\");\n        allDayBox.selectedProperty().bindBidirectional(resourcesView.showAllDayViewProperty());\n\n        CheckBox detailsBox = new CheckBox(\"Show details upon creation\");\n        detailsBox.selectedProperty().bindBidirectional(resourcesView.showDetailsUponEntryCreationProperty());\n\n        CheckBox flipBox = new CheckBox(\"Enable start / end flip over\");\n        flipBox.selectedProperty().bindBidirectional(resourcesView.enableStartAndEndTimesFlipProperty());\n\n        Slider slider = new Slider();\n        slider.setMin(0);\n        slider.setMax(1);\n        slider.valueProperty().bindBidirectional(resourcesView.entryViewAvailabilityEditingOpacityProperty());\n\n        return new VBox(10, availabilityButton, new Label(\"View type\"), typeBox, layoutBox, datePicker, infiniteScrolling, adjustBox, memoryTestButton, new Label(\"Number of resources\"), numberOfResourcesBox, new Label(\"Number of days\"), daysBox, new Label(\"Clicks to create\"), clicksBox,\n                new Label(\"Availability Behaviour\"), behaviourBox, new Label(\"Availability Opacity\"), slider, new Label(\"Grid Type\"), gridTypeBox, scrollbarBox, timescaleBox, allDayBox, detailsBox, flipBox);\n    }\n\n    @Override\n    protected DateControl createControl() {\n        resourcesView = new ResourcesView();\n        resourcesView.setScrollingEnabled(false);\n        resourcesView.setType(Type.DATES_OVER_RESOURCES);\n        resourcesView.setNumberOfDays(5);\n        resourcesView.setCreateEntryClickCount(1);\n        resourcesView.setGridType(GridType.CUSTOM);\n        resourcesView.setEarlyLateHoursStrategy(EarlyLateHoursStrategy.HIDE);\n        resourcesView.getResources().setAll(createResources(3));\n        resourcesView.setShowDetailsUponEntryCreation(false);\n\n        return resourcesView;\n    }\n\n    private List<Resource<String>> createResources(int count) {\n        ObservableList<Resource<String>> result = FXCollections.observableArrayList();\n        switch (count) {\n            case 1:\n                result.addAll(create(\"Dirk\", Style.STYLE1));\n                break;\n            case 2:\n                result.addAll(create(\"Dirk\", Style.STYLE1), create(\"Katja\", Style.STYLE2));\n                break;\n            case 3:\n                result.addAll(create(\"Dirk\", Style.STYLE1), create(\"Katja\", Style.STYLE2), create(\"Philip\", Style.STYLE3));\n                break;\n            case 4:\n                result.addAll(create(\"Dirk\", Style.STYLE1), create(\"Katja\", Style.STYLE2), create(\"Philip\", Style.STYLE3), create(\"Jule\", Style.STYLE4));\n                break;\n            case 5:\n                result.addAll(create(\"Dirk\", Style.STYLE1), create(\"Katja\", Style.STYLE2), create(\"Philip\", Style.STYLE3), create(\"Jule\", Style.STYLE4), create(\"Armin\", Style.STYLE5));\n                break;\n        }\n\n        return result;\n    }\n\n    private Resource<String> create(String name, Style style) {\n        Resource<String> resource = new Resource(name);\n        resource.getAvailabilityCalendar().setName(\"Availability of \" + name);\n//        resource.getCalendars().get(0).setStyle(style);\n//        resource.getCalendars().get(0).setUserObject(resource);\n//        resource.getCalendarSources().get(0).getCalendars().add(new Calendar(\"Second\", resource));\n        fillAvailabilities(resource.getAvailabilityCalendar());\n        return resource;\n    }\n\n    private void fillAvailabilities(Calendar calendar) {\n        LocalDate date = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));\n        for (int i = 0; i < 14; i++) {\n            // fourteen days is enough for this demo\n            Entry morning = new Entry(\"Morning\");\n            morning.setInterval(date, LocalTime.MIN, date, LocalTime.of(8, 0));\n            calendar.addEntry(morning);\n            Entry noon = new Entry(\"Noon\");\n            noon.setInterval(date, LocalTime.of(12, 0), date, LocalTime.of(13, 0));\n            calendar.addEntry(noon);\n            Entry evening = new Entry(\"Evening\");\n            evening.setInterval(date, LocalTime.of(18, 0), date, LocalTime.MAX);\n            calendar.addEntry(evening);\n            date = date.plusDays(1);\n        }\n    }\n\n    class HelloDayViewCalendar extends Calendar {\n\n        final Random dataRandom = new Random();\n\n        public HelloDayViewCalendar(long dataSeed) {\n            dataRandom.setSeed(dataSeed);\n        }\n\n        public void generateBaseEntries() {\n            createEntries(LocalDate.now(), Entry::new);\n            createEntries(LocalDate.now().plusDays(1), Entry::new);\n            createEntries(LocalDate.now().plusDays(2), Entry::new);\n            createEntries(LocalDate.now().plusDays(3), Entry::new);\n            createEntries(LocalDate.now().plusDays(4), Entry::new);\n        }\n\n        public void generateTopEntries() {\n            createEntries(LocalDate.now(), Entry::new);\n        }\n\n        private <T extends Entry<?>> void createEntries(LocalDate startDate, Supplier<T> entryProducer) {\n            for (int j = 0; j < 5 + (int) (dataRandom.nextDouble() * 4); j++) {\n                T entry = entryProducer.get();\n                entry.changeStartDate(startDate);\n                entry.changeEndDate(startDate);\n\n                String s = entry.getClass().getSimpleName();\n                entry.setTitle(s + (j + 1));\n\n                int hour = (int) (dataRandom.nextDouble() * 23);\n                int durationInHours = Math.max(1, Math.min(24 - hour, (int) (dataRandom.nextDouble() * 4)));\n\n                LocalTime startTime = LocalTime.of(hour, 0);\n                LocalTime endTime = startTime.plusHours(durationInHours);\n\n                entry.changeStartTime(startTime);\n                entry.changeEndTime(endTime);\n\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXSampler/src/main/java/module-info.java",
    "content": "open module com.calendarfx.sampler {\n    requires transitive org.controlsfx.fxsampler;\n    requires transitive javafx.graphics;\n\n    requires javafx.web;\n    requires fr.brouillard.oss.cssfx;\n    requires com.calendarfx.view;\n    requires atlantafx.base;\n\n    exports com.calendarfx.demo to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.entries to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.pages to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.performance to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.popover to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.print to org.controlsfx.fxsampler;\n    exports com.calendarfx.demo.views to org.controlsfx.fxsampler;\n\n    provides fxsampler.FXSamplerProject with com.calendarfx.demo.CalendarFXSamplerProject;\n}"
  },
  {
    "path": "CalendarFXSampler/src/main/resources/META-INF/services/fxsampler.FXSamplerProject",
    "content": "com.calendarfx.demo.CalendarFXSamplerProject"
  },
  {
    "path": "CalendarFXSchedulerApp/.gitignore",
    "content": "/target\n*.iml\n"
  },
  {
    "path": "CalendarFXSchedulerApp/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>scheduler</artifactId>\n    <name>CalendarFXScheduler</name>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>fr.brouillard.oss</groupId>\n            <artifactId>cssfx</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.scheduler.SchedulerApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "CalendarFXSchedulerApp/src/main/java/com/calendarfx/scheduler/SchedulerApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.scheduler;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.CalendarView.Page;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.WeekView;\nimport fr.brouillard.oss.cssfx.CSSFX;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\npublic class SchedulerApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        CalendarView calendarView = new CalendarView(Page.DAY, Page.WEEK);\n        calendarView.showWeekPage();\n        calendarView.setEnableTimeZoneSupport(false);\n        calendarView.setCreateEntryClickCount(1);\n\n        DetailedWeekView detailedWeekView = calendarView.getWeekPage().getDetailedWeekView();\n        WeekView weekView = detailedWeekView.getWeekView();\n        DayView dayView = calendarView.getDayPage().getDetailedDayView().getDayView();\n\n        detailedWeekView.setShowToday(false);\n        detailedWeekView.setEarlyLateHoursStrategy(EarlyLateHoursStrategy.HIDE);\n\n        // extra button for week page\n        ToggleButton editScheduleButton1 = new ToggleButton(\"Edit Schedule\");\n        editScheduleButton1.selectedProperty().bindBidirectional(weekView.editAvailabilityProperty());\n        ((Pane) calendarView.getWeekPage().getToolBarControls()).getChildren().add(editScheduleButton1);\n\n        // extra button for day page\n        ToggleButton editScheduleButton2 = new ToggleButton(\"Edit Schedule\");\n        editScheduleButton2.selectedProperty().bindBidirectional(dayView.editAvailabilityProperty());\n        ((Pane) calendarView.getDayPage().getToolBarControls()).getChildren().add(editScheduleButton2);\n\n        Calendar katja = new Calendar(\"Katja\");\n        Calendar dirk = new Calendar(\"Dirk\");\n        Calendar philip = new Calendar(\"Philip\");\n        Calendar jule = new Calendar(\"Jule\");\n        Calendar armin = new Calendar(\"Armin\");\n\n        katja.setShortName(\"K\");\n        dirk.setShortName(\"D\");\n        philip.setShortName(\"P\");\n        jule.setShortName(\"J\");\n        armin.setShortName(\"A\");\n\n        katja.setStyle(Style.STYLE1);\n        dirk.setStyle(Style.STYLE2);\n        philip.setStyle(Style.STYLE3);\n        jule.setStyle(Style.STYLE4);\n        armin.setStyle(Style.STYLE5);\n\n        CalendarSource familyCalendarSource = new CalendarSource(\"Family\");\n        familyCalendarSource.getCalendars().addAll(katja, dirk, philip, jule, armin);\n\n        calendarView.getCalendarSources().setAll(familyCalendarSource);\n        calendarView.setRequestedTime(LocalTime.now());\n\n        StackPane stackPane = new StackPane();\n        stackPane.getChildren().addAll(calendarView); // introPane);\n\n        Thread updateTimeThread = new Thread(\"Calendar: Update Time Thread\") {\n            @Override\n            public void run() {\n                while (true) {\n                    Platform.runLater(() -> {\n                        calendarView.setToday(LocalDate.now());\n                        calendarView.setTime(LocalTime.now());\n                    });\n\n                    try {\n                        // update every 10 seconds\n                        sleep(10000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n\n                }\n            }\n        };\n\n        updateTimeThread.setPriority(Thread.MIN_PRIORITY);\n        updateTimeThread.setDaemon(true);\n        updateTimeThread.start();\n\n        Scene scene = new Scene(stackPane);\n        CSSFX.start(scene);\n\n        primaryStage.setTitle(\"Calendar\");\n        primaryStage.setScene(scene);\n        primaryStage.setWidth(1300);\n        primaryStage.setHeight(1000);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSchedulerApp/src/main/java/com/calendarfx/scheduler/SchedulerAppLauncher.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.scheduler;\n\npublic class SchedulerAppLauncher {\n\n    public static void main(String[] args) {\n        System.setProperty(\"calendarfx.developer\", \"true\");\n        SchedulerApp.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXSchedulerApp/src/main/java/module-info.java",
    "content": "module com.calendarfx.scheduler {\n    requires transitive javafx.graphics;\n    requires fr.brouillard.oss.cssfx;\n    requires javafx.controls;\n    requires com.calendarfx.view;\n\n    exports com.calendarfx.scheduler;\n}"
  },
  {
    "path": "CalendarFXView/.gitignore",
    "content": "/target/\n/scenicView.properties\n*.iml\n\n"
  },
  {
    "path": "CalendarFXView/logging.properties",
    "content": "# To use this property file add the following command line argument:\n#    -Djava.util.logging.config.file=${project_loc}/logging.properties\n\n# Specify the handlers to create in the root logger\n# (all loggers are children of the root logger)\n# The following creates two handlers\n# handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler\nhandlers = java.util.logging.ConsoleHandler\n\n# Set the default logging level for the root logger\n.level = OFF\n\n# Set the default logging level for new ConsoleHandler instances\njava.util.logging.ConsoleHandler.level = FINE\n\n# Set the default logging level for new FileHandler instances\n# java.util.logging.FileHandler.level = ALL\n\n# Set the default formatter for new ConsoleHandler instances\njava.util.logging.ConsoleHandler.formatter = com.calendarfx.util.LoggingFormatter\n\n# LOG DOMAINS\ncom.calendarfx.config.level = OFF\ncom.calendarfx.model.level = OFF\ncom.calendarfx.events.level = OFF\ncom.calendarfx.view.level = OFF\ncom.calendarfx.search.level = OFF\ncom.calendarfx.editing.level = OFF\ncom.calendarfx.recurrence.level = OFF\ncom.calendarfx.printing.level = OFF\ncom.calendarfx.performance.level = OFF"
  },
  {
    "path": "CalendarFXView/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>view</artifactId>\n    <name>CalendarFXView</name>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>org.kordamp.ikonli</groupId>\n            <artifactId>ikonli-javafx</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.kordamp.ikonli</groupId>\n            <artifactId>ikonli-fontawesome-pack</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mnode.ical4j</groupId>\n            <artifactId>ical4j</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.controlsfx</groupId>\n            <artifactId>controlsfx</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.openjfx</groupId>\n            <artifactId>javafx-controls</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.dlsc.gemsfx</groupId>\n            <artifactId>gemsfx</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-validator</groupId>\n            <artifactId>commons-validator</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-logging</groupId>\n            <artifactId>commons-logging</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.asciidoctor</groupId>\n                <artifactId>asciidoctor-maven-plugin</artifactId>\n                <version>2.2.2</version>\n\n                <executions>\n                    <execution>\n                        <id>output-html</id>\n                        <phase>install</phase>\n                        <goals>\n                            <goal>process-asciidoc</goal>\n                        </goals>\n                    </execution>\n                </executions>\n\n                <configuration>\n                    <backend>html</backend>\n                    <outputFile>index.html</outputFile>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>3.2.0</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <configuration>\n                    <failOnError>false</failOnError>\n                    <force>true</force>\n                    <windowtitle>CalendarFX API</windowtitle>\n                    <detectLinks>false</detectLinks>\n                    <links>\n                        <link>https://openjfx.io/javadoc/14/</link>\n                    </links>\n\n                    <additionalJOptions>\n                        <additionalJOption>-J-Djavafx.javadoc=true</additionalJOption>\n                        <additionalJOption>-html5</additionalJOption>\n                    </additionalJOptions>\n                    <docfilessubdirs>true</docfilessubdirs>\n                    <tags>\n                        <tag>\n                            <name>defaultValue</name>\n                            <placement>a</placement>\n                            <head>Default Value:</head>\n                        </tag>\n                        <tag>\n                            <name>apiNote</name>\n                            <placement>a</placement>\n                            <head>API Note:</head>\n                        </tag>\n                        <tag>\n                            <name>implSpec</name>\n                            <placement>a</placement>\n                            <head>Implementation Requirements:</head>\n                        </tag>\n                        <tag>\n                            <name>implNote</name>\n                            <placement>a</placement>\n                            <head>Implementation Note:</head>\n                        </tag>\n                    </tags>\n                    <sourceFileExcludes>\n                        <sourceFileExclude>**\\/\\module-info.java</sourceFileExclude>\n                    </sourceFileExcludes>\n                </configuration>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.ow2.asm</groupId>\n                        <artifactId>asm</artifactId>\n                        <version>7.3.1</version>\n                    </dependency>\n                </dependencies>\n                <executions>\n                    <execution>\n                        <id>make-docs\n\n                        </id> <!-- this is used for inheritance merges -->\n                        <phase>package\n                        </phase> <!-- bind to the packaging phase -->\n                        <goals>\n                            <goal>aggregate</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>attach-javadocs</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "CalendarFXView/src/main/asciidoc/manual.adoc",
    "content": "= CalendarFX Developer Manual\nDirk Lemmermann <dlemmermann@gmail.com>\n:toc: left\n:source-highlighter: coderay\n:imagesdir: manual-images\n\nThis is the _CalendarFX_ developer manual. It aims to contain all the information required to quickly get a calendar UI into your application. If you notice any mistakes or if you are missing vital information then please let us know.\n\nimage::title.png[Calendar View,align=\"center\"]\n\n== Quick Start\n\nThe following section shows you how to quickly set up a JavaFX application that will show a complete calendar user interface. It includes a day view, a week view, a month view, a year view, an agenda view, a calendar selection view, and a search UI.\n\n[source,java,linenums]\n.CalendarApp.java\n----\npackage com.calendarfx.app;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.CalendarView;\n\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\npublic class CalendarApp extends Application {\n\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\n\t    CalendarView calendarView = new CalendarView(); // <1>\n\n\t\tCalendar birthdays = new Calendar(\"Birthdays\"); // <2>\n\t\tCalendar holidays = new Calendar(\"Holidays\");\n\n\t\tbirthdays.setStyle(Style.STYLE1); // <3>\n\t\tholidays.setStyle(Style.STYLE2);\n\n\t\tCalendarSource myCalendarSource = new CalendarSource(\"My Calendars\"); // <4>\n\t\tmyCalendarSource.getCalendars().addAll(birthdays, holidays);\n\n\t\tcalendarView.getCalendarSources().addAll(myCalendarSource); // <5>\n\n\t\tcalendarView.setRequestedTime(LocalTime.now());\n\n\t\tThread updateTimeThread = new Thread(\"Calendar: Update Time Thread\") {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\twhile (true) {\n\t\t\t\t\tPlatform.runLater(() -> {\n\t\t\t\t\t\tcalendarView.setToday(LocalDate.now());\n\t\t\t\t\t\tcalendarView.setTime(LocalTime.now());\n\t\t\t\t\t});\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// update every 10 seconds\n\t\t\t\t\t\tsleep(10000);\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tupdateTimeThread.setPriority(Thread.MIN_PRIORITY);\n\t\tupdateTimeThread.setDaemon(true);\n\t\tupdateTimeThread.start();\n\n\t\tScene scene = new Scene(calendarView);\n\t\tprimaryStage.setTitle(\"Calendar\");\n\t\tprimaryStage.setScene(scene);\n\t\tprimaryStage.setWidth(1300);\n\t\tprimaryStage.setHeight(1000);\n\t\tprimaryStage.centerOnScreen();\n\t\tprimaryStage.show();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tlaunch(args);\n\t}\n}\n----\n<1> Create the calendar view\n<2> Create one or more calendars\n<3> Set a style on each calendar (entries will use different colors)\n<4> Create a calendar source (e.g. \"Google\") and add calendars to it\n<5> Add calendars to the view\n\n== Model\n\nThe primary model classes in _CalendarFX_ are `CalendarSource`, `Calendar` and `Entry`. A calendar source often represents a calendar account, for example an account with \"Google Calendar\" (http://calendar.google.com). A calendar source consists of a list of calendars and each calendar manages any number of entries. An entry represents an event with a start date / time, an end date / time, and a time zone.\n\n=== Entry\n\nThe `Entry` class encapsulates all information that is required to display an event or an appointment in any of the calendar views included in _CalendarFX_.\n\n[.thumb]\nimage::entry.png[Calendar Entry,align=\"center\"]\n\nThe properties of an entry are:\n\nID:: a unique identifier\n\nTitle:: The title / name of the event or appointment (e.g. \"Dentist Appointment\")\n\nCalendar:: The calendar to which the entry belongs.\n\nHidden:: A flag that can be used to explicitly / manually hide an entry.\n\nInterval:: A complex data type grouping together start date / time, end date / time, and a time zone.\n\nLocation:: A free text description of a location, for example \"Manhatten, New York\". This information can be used by Geo services to return coordinates so that the UI can display a map if needed.\n\nFull Day:: A flag used to signal that the event is relevant for the entire day and that the start and end times are not relevant, for example a birthday or a holiday. Full day entries are displayed as shown below.\n\n[.thumb]\nimage::all-day-view.png[All Day View]\n\nMinimum Duration:: Ensures that the user can not create entries with a duration of less than, for example, 15 minutes.\n\nUser Object:: An arbitrary object which might be responsible for the creation of the entry in the first place.\n\nRecurrence Rule:: A text representation of a recurrence pattern according to RFC 2445 (\"RRULE:FREQ=DAILY\")\n\n[IMPORTANT]\n====\nThis last property is very interesting. It allows the entry to express that it defines a recurrence. The entry can specify that it will be repeated over and over again following a given pattern. For example: \"every Monday, Tuesday and Wednesday of every week until December 31st\". If an entry is indeed a recurring entry then it produces one or more \"recurrences\". These recurrences are created by the framework by invoking the `Entry.createRecurrence()` method. The result of this method is another Entry that will be configured with the same values as the source entry.\n====\n\nRecurrence:: A flag that expresses whether the entry represents a recurrence or not.\n\nRecurrence Source:: A reference to the original source entry.\n\nRecurrence ID:: If an entry represents a recurrence of a source entry then this property will store an additional ID, normally the date where the recurrence occurs.\n\nIn addition to these properties several read-only properties are available for convenience.\n\nMulti Day:: Needed y to easily determine if an entry spans multiple days. This information is constantly needed in various places of the framework for display / layout purposes.\n\nStart Date:: The date when the event begins (e.g. 5/12/2015).\n\nStart Time:: The time of day when the event begins (e.g. 2:15pm).\n\nEnd Date:: The date when the event ends (e.g. 8/12/2015).\n\nEnd Time:: The time of day when the event ends (e.g. 6:45pm).\n\n=== Calendar\n\nThe \"Calendar\" class is used to store entries in a binary interval tree. This data structure is not exposed to the outside. Instead methods exist on Calendar to add, remove, and find entries.\n\nThe following is a description of the main properties of the Calendar class:\n\nName:: The display name of the calendar, shown in several places within the UI.\n\nShort Name:: A short version of the calendar name. By default, it is set to be equal to the regular name, but if the application is using the swimlane layout then it might make sense to also define a short name due to limited space.\n\nRead-Only:: A flag for controlling whether entries can be added interactively in the UI or not. Setting this flag to false does not prevent the application itself to add entries.\n\nStyle:: Basically a name prefix for looking up different styles from the CSS file (calendar.css): _\"style1-\"_, _\"style2-\"_. The `Calendar` class defines an enumerator called `Style` that can be used to easily set the value of this property with one of the predefined styles.\n\nLook Ahead / Back Duration:: Two properties of type `java.time.Duration` that are used in combination with the current system time in order to create a time interval. The calendar class uses this time interval inside its `findEntries(String searchTerm)` method.\n\n==== Adding and Removing Entries\n\nTo add an entry simply call the `addEntry()` method on calendar.\nExample:\n[source,java,linenums]\n----\nCalendar calendar = ...\nEntry<String> dentistAppointment = new Entry<>(\"Dentist\");\ncalendar.addEntry(dentistAppointment);\n----\n\nTo remove an entry call the `removeEntry()` method on calendar.\n\n[source,java,linenums]\n----\nCalendar calendar = ...\nEntry<String> dentistAppointment = ...\ncalendar.removeEntry(dentistAppointment);\n----\n\nAlternatively you can simply set the calendar directly on the entry.\n\n[source,java,linenums]\n----\nCalendar calendar = ...\nEntry<String> dentistAppointment = ...\ndentistAppointment.setCalendar(calendar);\n----\n\nTo remove the entry from its calendar simply set the calendar to _null_.\n\n[source,java,linenums]\n----\nEntry<String> dentistAppointment = ...\ndentistAppointment.setCalendar(null);\n----\n\n\n==== Finding Entries for a Time Interval\n\nThe calendar class provides a `findEntries()` method which receives a start date, an end date, and a time zone. The result of invoking this method is a map where the keys are the dates for which entries were found and the values are lists of entries on that day.\n\n[NOTE]\n====\nThe result does not only contain entries that were previously added by calling the `addEntry()` method but also recurrence entries that were generated on-the-fly for those entries that define a recurrence rule.\n====\n\n[source,java,linenums]\n----\nCalendar calendar = ...\nMap<LocalDate, List<Entry<?>>> result = calendar.findEntries(LocalDate startDate,\n            LocalDate endDate, ZoneId zoneId)\n----\n\n==== Finding Entries for a Search String\n\nThe second `findEntries()` method accepts a search term as a parameter and is used to find entries that were previously added to the calendar and that match the term.\n\n[source,java,linenums]\n----\nCalendar calendar = ...\nList<Entry<?>> result = calendar.findEntries(String searchTerm)\n----\n\nTo find actual matches the method invokes the `Entry.matches(String)` method on all entries that are found within the time interval defined by the current date, the look back duration, and the look ahead duration.\n\n=== Calendar Source\n\nA calendar source is used for creating a group of calendars. A very typical scenario would be that a calendar source represents an online calendar account (e.g. Google calendar). Calendars can be added to a source by simply calling `mySource.getCalendars().add(myCalendar)`.\n\n=== Resource\n\nAn instance of `Resource` represents a business object that can be scheduled. Hence, the resource model class contains a calendar for scheduled entries / events / allocations and also a calendar that represents the resource's availability. The availability can be edited interactively in the `DayView` by setting the `editAvailability` property to true. It is important to note that it is the application's responsibility to make sense out of the created entries inside the availability calendar. While editing is in progress each newly created time interval will be directly added to the availability calendar.\n\n====\nThe `Resource` class is currently only used by the `ResourcesView`.\n====\n\n== Events\n\n_CalendarFX_ utilizes the JavaFX event model to inform the application about changes made in a calendar, about user interaction that might require loading of new data, and about user interaction that might require showing different views.\n\n=== Calendar Events\n\nAn event type that indicates that a change was made to the data is probably the most obvious type that anyone would expect from a UI framework. In _CalendarFX_ this event type is called `CalendarEvent`.\n\n.Calendar Event Type Hierarchy\n* `ANY` : the super event type\n** `CALENDAR_CHANGED` : \"something\" inside the calendar changed, usually causing rebuild of views (example: calendar batch updates finished)\n** `ENTRY_CHANGED` : the super type for changes made to an entry\n*** `ENTRY_CALENDAR_CHANGED` : the entry was assigned to a different calendar\n*** `ENTRY_FULL_DAY_CHANGED` : the full day flag was changed (from true to false or vice versa)\n*** `ENTRY_INTERVAL_CHANGED` : the time interval of the entry was changed (start date / time, end date / time)\n*** `ENTRY_LOCATION_CHANGED` : the location of the entry has changed\n*** `ENTRY_RECURRENCE_RULE_CHANGED` : the recurrence rule was modified\n*** `ENTRY_TITLE_CHANGED` : the entry title has changed\n*** `ENTRY_USER_OBJECT_CHANGED` : a new user object was set on the entry\n\nListeners for this event type can be added to calendars by calling:\n\n[source,java,linenums]\n----\nCalendar calendar = new Calendar(\"Demo\");\nEventHandler<CalendarEvent> handler = evt -> foo(evt);\ncalendar.addEventHandler(handler);\n----\n\n=== Load Events\n\nLoad events are used by the framework to signal to the application that the UI requires data for a specific time interval. This\ncan be very useful for implementing a lazy loading strategy. If the user switches from one month to another then an event of this\ntype will be fired and the time bounds on this event will be the first and the last day of that month. The `LoadEvent` type only\nsupports a single event type called `LOAD`.\n\nListeners for this event type can be registered on any date control:\n\n[source,java,linenums]\n----\nDayView view = new DayView();\nview.addEventHandler(LoadEvent.LOAD, evt -> foo(evt));\n----\n\n=== Request Events\n\nA unique event class is `RequestEvent`. It is used by the controls of the framework to signal to other framework controls that\nthe user wants to \"jump\" to another view. For example: the user clicks on the date shown for a day in the `MonthView` then the month view will fire a request event that informs the framework that the user wants to switch to the `DayView` to see more detail for that day.\n\n== DateControl\n\nA calendar user interface hardly ever consists of just a single control. They are composed of several views, some showing a single day or a week or\na month. In _CalendarFX_ the `CalendarView` control consists of dedicated \"pages\" for a day, a week, a month, or a full year. Each one of these pages consists of one or more subtypes of DateControl. The following image shows a simplified view of the scene graph / the containment hierarchy.\n\n[.thumb]\nimage::hierarchy.png[Hierarchy View,align=\"center\"]\n\nTo make all of these controls work together in harmony it is important that they share many properties. This is accomplished by JavaFX property binding. The class `DateControl` features a method called \"bind\" that ensures the dates and times shown by the controls are synchronized. But also that  many of the customization features (e.g. node factories) are shared.\n\nThe following listing shows the implementation of the `DateControl.bind()` method to give you an idea how much is bound within _CalendarFX_.\n\n[source,java,linenums]\n----\n    public final void bind(DateControl otherControl, boolean bindDate) {\n\n        // bind lists\n        Bindings.bindContentBidirectional(otherControl.getCalendarSources(), \n        \tgetCalendarSources());\n        Bindings.bindContentBidirectional(otherControl.getSelections(), \n        \tgetSelections());\n        Bindings.bindContentBidirectional(otherControl.getWeekendDays(), \n        \tgetWeekendDays());\n\n        // bind properties\n        Bindings.bindBidirectional(otherControl.entryFactoryProperty(), \n        \tentryFactoryProperty());\n        Bindings.bindBidirectional(otherControl.defaultCalendarProviderProperty(), \n        \tdefaultCalendarProviderProperty());\n        Bindings.bindBidirectional(otherControl.virtualGridProperty(), \n        \tvirtualGridProperty());\n        Bindings.bindBidirectional(otherControl.draggedEntryProperty(), \n        \tdraggedEntryProperty());\n        Bindings.bindBidirectional(otherControl.requestedTimeProperty(), \n        \trequestedTimeProperty());\n\n        Bindings.bindBidirectional(otherControl.selectionModeProperty(), \n        \tselectionModeProperty());\n        Bindings.bindBidirectional(otherControl.selectionModeProperty(), \n        \tselectionModeProperty());\n        Bindings.bindBidirectional(otherControl.weekFieldsProperty(), \n        \tweekFieldsProperty());\n        Bindings.bindBidirectional(otherControl.layoutProperty(), \n        \tlayoutProperty());\n\n        if (bindDate) {\n            Bindings.bindBidirectional(otherControl.dateProperty(), dateProperty());\n        }\n\n        Bindings.bindBidirectional(otherControl.todayProperty(), \n        \ttodayProperty());\n        Bindings.bindBidirectional(otherControl.zoneIdProperty(), \n        \tzoneIdProperty());\n\n        // edit callbacks\n        Bindings.bindBidirectional(\n        \totherControl.entryDetailsCallbackProperty(), \n        \tentryDetailsCallbackProperty());\n        Bindings.bindBidirectional(\n        \totherControl.dateDetailsCallbackProperty(), \n        \tdateDetailsCallbackProperty());\n        Bindings.bindBidirectional(\n        \totherControl.contextMenuCallbackProperty(), \n        \tcontextMenuCallbackProperty());\n        Bindings.bindBidirectional(\n        \totherControl.entryContextMenuCallbackProperty(), \n        \tentryContextMenuCallbackProperty());\n        Bindings.bindBidirectional(\n        \totherControl.calendarSourceFactoryProperty(), \n        \tcalendarSourceFactoryProperty());\n        Bindings.bindBidirectional(\n        \totherControl.entryDetailsPopOverContentCallbackProperty(), \n        \tentryDetailsPopOverContentCallbackProperty());\n    }\n----\n\n=== Class Hierarchy\n\n_CalendarFX_ ships with many built-in views for displaying calendar information. All of these views inherit from `DateControl`. The class\nhierarchy can be seen in the following image:\n\nimage::datecontrol.png[Class Hierarchy,align=\"center\"]\n\n\n=== Current Date, Time, and Today\n\nEach `DateControl` keeps track of the \"current date\" and \"today\". The current date is the date that the control is supposed to display to the user.\n\"Today\" is the date that the control assumes to be the actual date. \"Today\" defaults to the current system date (provided by the operating system), but\nit can be any date.\n\n\n[IMPORTANT]\n.Updating today and current time\n====\nThe \"today\" and \"time\" properties do not get updated by themselves. See the daemon thread created in the listing shown in the \"Quick Start\" section.\n====\n\n`DateControl` defines utility methods that allow for easy modification of the \"current\" date.\n\n[source,java,linenums]\n----\npublic void goToday();\npublic void goForward();\npublic void goBack();\n----\n\n\n=== Adding Calendars / Sources\n\nEven though the `DateControl` class provides a `getCalendars()` method this is not the place where calendars are being added. Instead,\nalways create calendar sources, add calendars to them, and then add the sources to the control. The \"calendars\" list is a read-only\nflat list representation of all calendars in all calendar sources. The \"calendars\" list gets updated by the framework.\n\n[source,java,linenums]\n.Adding Calendars\n----\nCalendar katja = new Calendar(\"Katja\");\nCalendar dirk = new Calendar(\"Dirk\");\n\nCalendarSource familyCalendarSource = new CalendarSource(\"Family\");\nfamilyCalendarSource.getCalendars().addAll(katja, dirk);\n\nCalendarView calendarView = new CalendarView();\ncalendarView.getCalendarSources().setAll(familyCalendarSource);\n----\n\n=== Customizing or Replacing the PopOver\n\nThe `DateControl` class has built-in support for displaying a `PopOver` control when the user double-clicks on a calendar entry. The content node of this `PopOver` can be replaced. It is normally used to show some basic entry details (e.g. start / end date, title, event location) but applications might have defined specialized entries with custom properties that require additional UI elements. This can be accomplished by the help of the `PopOver` content node factory.\n\n[source,java,linenums]\n.PopOver Content Node Factory\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setEntryDetailsPopOverContentCallback(param -> new MyCustomPopOverContentNode());\n----\n\nIf an application does not want to use the `PopOver` at all but instead display a standard dialog then there is a way of doing that, too. Simply\nregister an entry details callback.\n\n[source,java,linenums]\n.Entry Details Callback\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setEntryDetailsCallback(param -> new MyCustomEntryDialog());\n----\n\nThese two callbacks normally work hand in hand. The default implementation of the entry details callback is producing a `PopOver` and sets the content\nnode on the PopOver via the help of the content node callback.\n\n=== Context Menu Support\n\nA common place for customization are context menus. The `DateControl` class produces a context menu via specialized callbacks. One callback is used\nto produce a menu for a given calendar entry, the second callback is used when the user triggers the context menu by clicking in the background\nof a `DateControl`.\n\n[source,java,linenums]\n.PopOver Content Node Factory\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setEntryContextMenuCallback(param -> new MyEntryContextMenu());\ncalendarView.setContextMenuCallback(param -> new MyContextMenu());\n----\n\n[IMPORTANT]\n.Context Menus\n====\nThe context menu callbacks are automatically shared among all date controls that are bound to each other. The same context menu code will execute for\ndifferent views, the `DayView`, the `MonthView`, and so on. This means that the code that builds the context menu will need to check the parameter object\nthat was passed to the callback to configure itself appropriately.\n\nThe same is true for basically all callbacks used by the DateControl.\n====\n\n\n=== Creating Entries\n\nThe user can create new entries by double-clicking anywhere inside a `DateControl`. The actual work of creating a new entry instance is then delegated to a specialized entry factory that can be set on `DateControl`.\n\n[source,java,linenums]\n.Entry Factory\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setEntryFactory(param -> new MyEntryFactory());\n----\n\nOnce the entry factory has returned the new entry it will be added to the calendar that is being returned by the \"default calendar\" provider. This provider is also customizable via a callback.\n\n[source,java,linenums]\n.Default Calendar Provider\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setDefaultCalendarProvider(param -> new MyDefaultCalendarProvider());\n----\n\nBesides the double click creation the application can also programmatically request the DateControl to create a new entry at a given point in time. Two methods are available for this: createEntryAt(ZonedDateTime) and createEntryAt(ZonedDateTime, Calendar). The second method will ensure that the entry will be added to the given calendar while the first method will invoke the default calendar provider.\n\n=== Creating Calendar Sources\n\nThe user might also wish to add another calendar source to the application. In this case the DateControl will invoke the calendar source factory. The default implementation of this factory does nothing more than to create a new instance of the standard CalendarSource class. Applications are free to return a specialization of CalendarSource instead (e.g. GoogleCalendarAccount). A custom factory might even prompt the user first with a dialog, e.g. to request user credentials.\n\n[source,java,linenums]\n.Default Calendar Provider\n----\nCalendarView calendarView = new CalendarView();\ncalendarView.setCalendarSourceFactory(param -> new MyCalendarSource());\n----\n\nThe calendar source factory gets invoked when the method `DateControl.createCalendarSource()` gets invoked. The `CalendarView` class already provides a button\nin its toolbar that will call this method.\n\n== Entry Views\n\nEntry views are JavaFX nodes that are representing calendar entries. There are several types, all extending `EntryViewBase`:\n\nDay Entry View:: Shown inside a `DayView` or `WeekDayView` control. These views can be customized by subclassing `DayEntryViewSkin` and overriding the `createContent()` method.\nAll Day Entry View:: Shown inside the `AllDayView` control.\nMonth Entry View:: Shown inside the `MonthView` control.\n\n== Standard Calendar Views\n\nThe most fundamental views inside _CalendarFX_ are of course the views used to display a day (24 hours), an entire week, a month, and a year.\n\nDayView:: Shows a 24-hour time period vertically. The control has several options that can be used to influence the layout of the hours. E.g.: it is possible to define hour ranges where the time will be compressed in order to save space on the screen (early and late hours are often not relevant). The view can also specify whether it wants to always show a fixed number of hours or a fixed height for each hour.\n\n[.thumb]\nimage::day-view.png[Day View,align=\"center\"]\n\nDetailedDayView:: wraps the `DayView` control with several additional controls: an `AllDayView`, a `TimeScaleView`, a `CalendarHeaderView`, a `ScrollBar` and an (optional)\n`AgendaView`.\n\n[.thumb]\nimage::detailed-day-view-agenda.png[Detailed Day View,align=\"center\"]\n\nWeekView:: The name of this control is somewhat misleading, because it can show any number of `WeekDayView` instances, not just 5 or 7 but also 14 (two weeks) or 21 (three weeks). In this view entries can be easily edited to span multiple days.\n\n[.thumb]\nimage::week-view.png[Week View,align=\"center\"]\n\nDetailedWeekView:: same concept as the `DetailedDayView`. This view wraps the `WeekView` and adds several other controls.\n\n[.thumb]\nimage::detailed-week-view.png[Detailed Week View,align=\"center\"]\n\nMonthView:: Shows up to 31 days for the current month plus some days of the previous and the next month.\n\n[.thumb]\nimage::month-view.png[Month View,align=\"center\"]\n\nMonthSheetView:: Shows several months in a column layout. Weekdays can be aligned so that the same weekdays are always next to each other. A customizable\ncell factory is used to create the date cells. Several default implementations are included in _CalendarFX_: simple date cell, usage date cell, badge date cell,\ndetail date cell.\n\n[.thumb]\nimage::month-sheet-view.png[Month Sheet View,align=\"center\"]\n\n[.thumb]\nimage::month-sheet-view-aligned.png[Month Sheet View Aligned,align=\"center\"]\n\nYearView:: Shows twelve `YearMonthView` instances.\n\n[.thumb]\nimage::year-view.png[Year View,align=\"center\"]\n\nYearMonthView:: Sort of a date picker control. 12 instances of this control are used to build up the `YearPage` control. This control provides many properties for easy\ncustomization. The month label, the year label, and the arrow buttons can be hidden. A cell factory can be set to customize the appearance of each day, and so on.\n\n[.thumb]\nimage::date-picker.png[Year Month View,align=\"center\"]\n\nAllDayView:: Just like the `WeekView` this control can also span multiple days. It is being used as a header for the `DayView` inside the `DayPage` and also for the `WeekView` inside the `WeekPage`. The control displays calendar entries that have their \"full day\" property set to true.\n\n[.thumb]\nimage::all-day-view.png[All Day View,align=\"center\"]\n\nCalendarHeaderView:: Displays the names of all currently visible calendars, but only when the `DateControl` has its layout set to `SWIMLANE` and not to `STANDARD`.\n\n[.thumb]\nimage::calendar-header-view.png[Calendar Header View,align=\"center\"]\n\n== Calendar Pages\n\nCalendar pages are complex controls that are composed of several controls, many of them `DateControl` instances. All pages provide controls to navigate to different\ndates or to quickly jump to \"Today\". Each page also shows a title with the current date shown. The `CalendarView` class manages one instance of each page type to let the\nuser switch from a day, to a week, to a month, to a year.\n\nDayPage:: Shows an `AgendaView`, a `DetailedDayView`, and a `YearMonthView`. This page is designed to give the user a quick overview of what is going on today and\nin the near future (agenda).\n\n[.thumb]\nimage::day-page.png[Day Page,align=\"center\",border=\"1\"]\n\nWeekPage:: Composed of a `DetailedWeekView`.\n\n[.thumb]\nimage::week-page.png[Week Page,align=\"center\"]\n\nMonthPage:: Shows a single `MonthView` control.\n\n[.thumb]\nimage::month-page.png[Month Page,align=\"center\"]\n\nYearPage:: Shows a `YearView` with twelve `YearMonthView` sub-controls. Alternatively can switch to a `MonthSheetView`.\n\n[.thumb]\nimage::year-page.png[Year Page using YearView,align=\"center\"]\n\n[.thumb]\nimage::year-page-2.png[Year Page using MonthSheetView,align=\"center\"]\n\n== Resource Scheduling Views\n\nAnother category of views is used for scheduling resource allocations. These are commonly used by scheduling software, e.g. a customer appointment application for a garage, a hairdresser, and so on. The\n\n=== ResourcesView\n\nThe class `ResourcesView` displays one or more days for one or more resources. The view can either display one or more resources for a given\nday (`ResourceView.Type.RESOURCES_OVER_DATES`) or one or more days for a given resource (`ResourceView.Type.DATES_OVER_RESOURCE`). Each one of\nthese options can be configured to show one or more dates and one or more resources.\n\nThis screenshot shows the resources view when the type of the view has been set to \"resources over dates\".\n\n[.thumb]\nimage::resources-view-resources-over-dates.png[ResourcesView - Resources over Dates,align=\"center\"]\n\nThe next screenshot shows the resources view when the type of the view has been set to \"dates over resources\".\n\n[.thumb]\nimage::resources-view-dates-over-resources.png[esourcesView - Dates over Resources,align=\"center\"]\n\nBy default, the calendar entries will become semi-transparent when the user switches to the \"edit availability\" mode. This behaviour can be configured so that either the entries stay completely visible or they are completely hidden. The following screenshot shows the situation where the user is editing the resources' availability and the already existing calendar entries become semi-transparent.\n\n[.thumb]\nimage::resources-view-availability.png[esourcesView - Availability Editing,align=\"center\"]\n\n\n== Developer Console\n\n_CalendarFX_ supports a special system property called `calendarfx.developer`. If this property is set to `true` then a developer console is being added to the skin of `CalendarView`. The console can be made visible by pressing `SHORTCUT-D`. The console is a standard _CalendarFX_ control and you can also add it directly to your application for development purposes.\n\n[.thumb]\nimage::developer-console.png[Developer Console,align=\"center\"]\n\n== Logging\n\n_CalendarFX_ uses the standard java logging api for its logging. The logging settings and the available loggers can be found inside the file `logging.properties`. _CalendarFX_ uses domains for logging and not packages or classes. Several domains are available: view, model, editing, recurrence, etc...\n\n== Internationalization (i18n)\n\nThe default resource bundle of _CalendarFX_ is English. Additional bundles include German, Spanish, French, Italian, Portuguese (Brazil), and Czech.\nAll can be found in the distribution (misc/messages.properties, misc/messages_de.properties, etc...). Please submit a pull request to add another\nlanguage to _CalendarFX_.\n\n== Known Issues\n\n* There is currently no support for defining exceptions for recurrence rules. In most calendar applications, when the user edits a recurrent entry, the user will be asked whether he wants to change just this one recurrence or the whole series. This feature is currently not supported but will be in one of the next releases.\n\n* In `SwimLane` layout it would be nice if the user could drag an entry horizontally from one column / calendar to another. This is currently not supported. We will investigate if this can be added in one of the next releases.\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/Calendar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport com.calendarfx.view.DateControl;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.event.Event;\nimport javafx.event.EventDispatchChain;\nimport javafx.event.EventHandler;\nimport javafx.event.EventTarget;\nimport net.fortuna.ical4j.model.Recur;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport static com.calendarfx.model.CalendarEvent.CALENDAR_CHANGED;\nimport static com.calendarfx.model.CalendarEvent.ENTRY_CHANGED;\nimport static com.calendarfx.util.LoggingDomain.MODEL;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.logging.Level.FINE;\nimport static java.util.logging.Level.FINER;\n\n/**\n * A calendar is responsible for storing calendar entries. It provides methods\n * for adding, removing, and querying entries. A calendar also defines a visual\n * style / color theme that will be used throughout the UI controls. Calendars\n * fire events whenever entries are added or removed. Calendars are grouped together\n * inside a {@link CalendarSource}. These calendar sources are then added to\n * {@link DateControl#getCalendarSources()}.\n *\n * <h2>Example</h2>\n * <pre>\n *     {@code\n *     // create the calendar and listen to all changes\n *     Calendar calendar = new Calendar(\"Home\");\n *     calendar.addEventHandler(CalendarEvent.ANY, evt -> handleEvent(evt));\n *\n *     // create the calendar source and attach the calendar\n *     CalendarSource source = new CalendarSource(\"Online Calendars\");\n *     source.getCalendars().add(calendar);\n *\n *     // attach the source to the date control / calendar view.\n *     CalendarView view = new CalendarView();\n *     view.getCalendarSources().add(source);\n *     }\n * </pre>\n *\n * @param <T> the type of the (optional) user object\n */\npublic class Calendar<T> implements EventTarget {\n\n    /**\n     * Predefined visual styles for calendars. The actual CSS settings for these\n     * styles can be found in the framework stylesheet, prefixed with \"style1-\",\n     * \"style2-\", etc. The picture below shows the colors used for the various\n     * styles.\n     *\n     * <img src=\"doc-files/styles.png\" alt=\"Styles\">\n     *\n     * @see Calendar#setStyle(Style)\n     */\n    public enum Style {\n\n        /**\n         * Default style \"1\".\n         */\n        STYLE1,\n\n        /**\n         * Default style \"2\".\n         */\n        STYLE2,\n\n        /**\n         * Default style \"3\".\n         */\n        STYLE3,\n\n        /**\n         * Default style \"4\".\n         */\n        STYLE4,\n\n        /**\n         * Default style \"5\".\n         */\n        STYLE5,\n\n        /**\n         * Default style \"6\".\n         */\n        STYLE6,\n\n        /**\n         * Default style \"7\".\n         */\n        STYLE7;\n\n        /**\n         * Returns a style for the given ordinal. This method is implemented\n         * with a roll over strategy: the final ordinal value is the given\n         * ordinal value modulo the number of elements in this enum.\n         *\n         * @param ordinal the ordinal value for which to return a style\n         * @return a style, guaranteed to be non-null\n         */\n        public static Style getStyle(int ordinal) {\n            return Style.values()[ordinal % Style.values().length];\n        }\n    }\n\n    private final IntervalTree<Entry<?>> intervalTree = new IntervalTree<>();\n\n    /**\n     * Constructs a new calendar.\n     */\n    public Calendar() {\n        addEventHandler(evt -> {\n            Entry<?> entry = evt.getEntry();\n            if (evt.getEventType().getSuperType().equals(ENTRY_CHANGED) && entry.isRecurrence()) {\n                updateRecurrenceSourceEntry(evt, entry.getRecurrenceSourceEntry());\n            }\n        });\n    }\n\n\n    /**\n     * Constructs a new calendar with the given name.\n     *\n     * @param name the name of the calendar\n     * @param userObject an optional user object\n     */\n    public Calendar(String name, T userObject) {\n        this();\n\n        setName(name);\n        if (name != null) {\n            setShortName(!name.isEmpty() ? name.substring(0, 1) : \"\");\n        }\n\n        setUserObject(userObject);\n    }\n\n    /**\n     * Constructs a new calendar with the given name.\n     *\n     * @param name the name of the calendar\n     */\n    public Calendar(String name) {\n        this(name, null);\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private void updateRecurrenceSourceEntry(CalendarEvent evt, Entry source) {\n        Entry recurrence = evt.getEntry();\n        if (evt.getEventType().equals(CalendarEvent.ENTRY_INTERVAL_CHANGED)) {\n            Interval oldInterval = evt.getOldInterval();\n            Interval newInterval = calculateSourceBoundsFromRecurrenceBounds(source, recurrence, oldInterval);\n            source.setInterval(newInterval);\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_LOCATION_CHANGED)) {\n            source.setLocation(recurrence.getLocation());\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_RECURRENCE_RULE_CHANGED)) {\n            source.setRecurrenceRule(recurrence.getRecurrenceRule());\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_TITLE_CHANGED)) {\n            source.setTitle(recurrence.getTitle());\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_USER_OBJECT_CHANGED)) {\n            source.setUserObject(recurrence.getUserObject());\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_CALENDAR_CHANGED)) {\n            source.setCalendar(recurrence.getCalendar());\n        } else if (evt.getEventType().equals(CalendarEvent.ENTRY_FULL_DAY_CHANGED)) {\n            source.setFullDay(recurrence.isFullDay());\n        }\n    }\n\n    private Interval calculateSourceBoundsFromRecurrenceBounds(Entry<?> source, Entry<?> recurrence, Interval oldInterval) {\n        ZonedDateTime recurrenceStart = recurrence.getStartAsZonedDateTime();\n        ZonedDateTime recurrenceEnd = recurrence.getEndAsZonedDateTime();\n\n        Duration startDelta = Duration.between(oldInterval.getStartZonedDateTime(), recurrenceStart);\n        Duration endDelta = Duration.between(oldInterval.getEndZonedDateTime(), recurrenceEnd);\n\n        ZonedDateTime sourceStart = source.getStartAsZonedDateTime();\n        ZonedDateTime sourceEnd = source.getEndAsZonedDateTime();\n\n        sourceStart = sourceStart.plus(startDelta);\n        sourceEnd = sourceEnd.plus(endDelta);\n\n        return new Interval(sourceStart.toLocalDate(), sourceStart.toLocalTime(), sourceEnd.toLocalDate(), sourceEnd.toLocalTime(), recurrence.getZoneId());\n    }\n\n    /**\n     * Gets the earliest time used by this calendar, that means the start of the\n     * first entry stored.\n     *\n     * @return An instant representing the earliest time, can be null if no\n     * entries are contained.\n     */\n    public final Instant getEarliestTimeUsed() {\n        return intervalTree.getEarliestTimeUsed();\n    }\n\n    /**\n     * Gets the latest time used by this calendar, that means the end of the\n     * last entry stored.\n     *\n     * @return An instant representing the latest time, can be null if no\n     * entries are contained.\n     */\n    public final Instant getLatestTimeUsed() {\n        return intervalTree.getLatestTimeUsed();\n    }\n\n    private boolean batchUpdates;\n\n    private boolean dirty;\n\n    /**\n     * Tells the calendar that the application will perform a large number of changes.\n     * While batch updates in progress the calendar will stop to fire events. To finish\n     * this mode the application has to call {@link #stopBatchUpdates()}.\n     */\n    public final void startBatchUpdates() {\n        batchUpdates = true;\n        dirty = false;\n    }\n\n    /**\n     * Tells the calendar that the application is done making big changes. Invoking\n     * this method will trigger a calendar event of type {@link CalendarEvent#CALENDAR_CHANGED} which\n     * will then force an update of the views.\n     */\n    public final void stopBatchUpdates() {\n        batchUpdates = false;\n\n        if (dirty) {\n            dirty = false;\n            fireEvent(new CalendarEvent(CalendarEvent.CALENDAR_CHANGED, this));\n        }\n    }\n\n    /**\n     * Queries the calendar for all entries within the time interval defined by\n     * the start date and end date.\n     *\n     * @param startDate the start of the time interval\n     * @param endDate   the end of the time interval\n     * @param zoneId    the time zone for which to find entries\n     * @return a map filled with list of entries for given days\n     */\n    public final Map<LocalDate, List<Entry<?>>> findEntries(LocalDate startDate, LocalDate endDate, ZoneId zoneId) {\n        fireEvents = false;\n\n        Map<LocalDate, List<Entry<?>>> result;\n\n        try {\n            result = doGetEntries(startDate, endDate, zoneId);\n        } finally {\n            fireEvents = true;\n        }\n\n        return result;\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private Map<LocalDate, List<Entry<?>>> doGetEntries(LocalDate startDate, LocalDate endDate, ZoneId zoneId) {\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(getName() + \": getting entries from \" + startDate + \" until \" + endDate + \", zone = \" + zoneId);\n        }\n\n        ZonedDateTime st = ZonedDateTime.of(startDate, LocalTime.MIN, zoneId);\n        ZonedDateTime et = ZonedDateTime.of(endDate, LocalTime.MAX, zoneId);\n\n        Collection<Entry<?>> intersectingEntries = intervalTree.getIntersectingObjects(st.toInstant(), et.toInstant());\n\n        if (intersectingEntries.isEmpty()) {\n            if (MODEL.isLoggable(FINE)) {\n                MODEL.fine(getName() + \": found no entries\");\n            }\n            return Collections.emptyMap();\n        }\n\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(getName() + \": found \" + intersectingEntries.size() + \" entries\");\n        }\n\n        Map<LocalDate, List<Entry<?>>> result = new HashMap<>();\n        for (Entry<?> entry : intersectingEntries) {\n\n            if (entry.isRecurring()) {\n                String recurrenceRule = entry.getRecurrenceRule().replaceFirst(\"^RRULE:\", \"\");\n\n                LocalDate utilStartDate = entry.getStartDate();\n\n                try {\n                    LocalDate utilEndDate = et.toLocalDate();\n\n                    List<LocalDate> dateList = new Recur(recurrenceRule).getDates(utilStartDate, utilEndDate);\n\n                    for (LocalDate repeatingDate : dateList) {\n                        ZonedDateTime zonedDateTime = ZonedDateTime.of(repeatingDate, LocalTime.MIN, zoneId);\n\n                        Entry recurrence = entry.createRecurrence();\n                        recurrence.setId(entry.getId());\n                        recurrence.getProperties().put(\"com.calendarfx.recurrence.source\", entry);\n                        recurrence.getProperties().put(\"com.calendarfx.recurrence.id\", zonedDateTime.toString());\n                        recurrence.setRecurrenceRule(entry.getRecurrenceRule());\n\n                        // update the recurrence interval\n                        LocalDate recurrenceStartDate = zonedDateTime.toLocalDate();\n                        LocalDate recurrenceEndDate = recurrenceStartDate.plus(entry.getStartDate().until(entry.getEndDate()));\n                        recurrence.setInterval(entry.getInterval().withDates(recurrenceStartDate, recurrenceEndDate));\n\n                        recurrence.setUserObject(entry.getUserObject());\n                        recurrence.setTitle(entry.getTitle());\n                        recurrence.setMinimumDuration(entry.getMinimumDuration());\n                        recurrence.setFullDay(entry.isFullDay());\n                        recurrence.setLocation(entry.getLocation());\n                        recurrence.setCalendar(this);\n\n                        addEntryToResult(result, recurrence, startDate, endDate);\n                    }\n\n                } catch (IllegalArgumentException | DateTimeParseException e) {\n                    e.printStackTrace();\n                }\n            } else {\n                addEntryToResult(result, entry, startDate, endDate);\n            }\n        }\n\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(getName() + \": found entries for \" + result.size() + \" different days\");\n        }\n\n        result.values().forEach(Collections::sort);\n\n        return result;\n    }\n\n    /*\n     * Assign the given entry to each date that it intersects with in the given search interval.\n     */\n    private void addEntryToResult(Map<LocalDate, List<Entry<?>>> result, Entry<?> entry, LocalDate startDate, LocalDate endDate) {\n        LocalDate entryStartDate = entry.getStartDate();\n        LocalDate entryEndDate = entry.getEndDate();\n\n        // entry does not intersect with time interval\n        if (entryEndDate.isBefore(startDate) || entryStartDate.isAfter(endDate)) {\n            return;\n        }\n\n        if (entryStartDate.isAfter(startDate)) {\n            startDate = entryStartDate;\n        }\n\n        if (entryEndDate.isBefore(endDate)) {\n            endDate = entryEndDate;\n        }\n\n        LocalDate date = startDate;\n        do {\n            result.computeIfAbsent(date, it -> new ArrayList<>()).add(entry);\n            date = date.plusDays(1);\n        } while (!date.isAfter(endDate));\n    }\n\n    private final ObjectProperty<Duration> lookAheadDuration = new SimpleObjectProperty<>(this, \"lookAheadDuration\", Duration.ofDays(730));\n\n    /**\n     * Stores a time duration used for the entry search functionality of this\n     * calendar. The look ahead and the look back durations limit the search to\n     * the time interval [now - lookBackDuration, now + lookAheadDuration]. The\n     * default value of this property is 730 days (2 years).\n     *\n     * @return the look ahead duration\n     * @see #findEntries(String)\n     */\n    public final ObjectProperty<Duration> lookAheadDurationProperty() {\n        return lookAheadDuration;\n    }\n\n    /**\n     * Sets the value of {@link #lookAheadDurationProperty()}.\n     *\n     * @param duration the look ahead duration\n     */\n    public final void setLookAheadDuration(Duration duration) {\n        requireNonNull(duration);\n        lookAheadDurationProperty().set(duration);\n    }\n\n    /**\n     * Returns the value of {@link #lookAheadDurationProperty()}.\n     *\n     * @return the look ahead duration\n     */\n    public final Duration getLookAheadDuration() {\n        return lookAheadDurationProperty().get();\n    }\n\n    private final ObjectProperty<Duration> lookBackDuration = new SimpleObjectProperty<>(this, \"lookBackDuration\", Duration.ofDays(730));\n\n    /**\n     * Stores a time duration used for the entry search functionality of this\n     * calendar. The look ahead and the look back durations limit the search to\n     * the time interval [now - lookBackDuration, now + lookAheadDuration]. The\n     * default value of this property is 730 days (2 years).\n     *\n     * @return the look back duration\n     * @see #findEntries(String)\n     */\n    public final ObjectProperty<Duration> lookBackDurationProperty() {\n        return lookBackDuration;\n    }\n\n    /**\n     * Sets the value of {@link #lookBackDurationProperty()}.\n     *\n     * @param duration the look back duration\n     */\n    public final void setLookBackDuration(Duration duration) {\n        requireNonNull(duration);\n        lookBackDurationProperty().set(duration);\n    }\n\n    /**\n     * Returns the value of {@link #lookBackDurationProperty()}.\n     *\n     * @return the look back duration\n     */\n    public final Duration getLookBackDuration() {\n        return lookBackDurationProperty().get();\n    }\n\n    /**\n     * Queries the calendar for entries that match the given search text. The method\n     * can be overridden to implement custom find / search strategies.\n     *\n     * @param searchText the search text\n     * @return a list of entries that match the search\n     * @see Entry#matches(String)\n     */\n    public List<Entry<?>> findEntries(String searchText) {\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(getName() + \": getting entries for search term: \"\n                    + searchText);\n        }\n\n        Instant horizonStart = Instant.now().minus(getLookBackDuration());\n        Instant horizonEnd = Instant.now().plus(getLookAheadDuration());\n\n        ZoneId zoneId = ZoneId.systemDefault();\n\n        ZonedDateTime st = ZonedDateTime.ofInstant(horizonStart, zoneId);\n        ZonedDateTime et = ZonedDateTime.ofInstant(horizonEnd, zoneId);\n\n        List<Entry<?>> result = new ArrayList<>();\n\n        Map<LocalDate, List<Entry<?>>> map = findEntries(st.toLocalDate(), et.toLocalDate(), zoneId);\n        for (List<Entry<?>> list : map.values()) {\n            for (Entry<?> entry : list) {\n                if (entry.matches(searchText)) {\n                    result.add(entry);\n                }\n            }\n        }\n\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(getName() + \": found \" + result.size() + \" entries\");\n        }\n\n        return result;\n    }\n\n    /**\n     * Removes all entries from the calendar. Fires an\n     * {@link CalendarEvent#CALENDAR_CHANGED} event.\n     */\n    public final void clear() {\n        intervalTree.clear();\n        fireEvent(new CalendarEvent(CALENDAR_CHANGED, this));\n    }\n\n    // support for adding entries\n\n    /**\n     * Adds the given entry to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entry the entry to add\n     */\n    public final void addEntry(Entry<?> entry) {\n        addEntries(entry);\n    }\n\n    /**\n     * Adds the given entries to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to add\n     */\n    public final void addEntries(Entry<?>... entries) {\n        if (entries != null) {\n            for (Entry<?> entry : entries) {\n                entry.setCalendar(this);\n            }\n        }\n    }\n\n    /**\n     * Adds the given entries to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the collection of entries to add\n     */\n    public final void addEntries(Collection<Entry<?>> entries) {\n        if (entries != null) {\n            entries.forEach(this::addEntry);\n        }\n    }\n\n    /**\n     * Adds the entries returned by the iterator to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to add\n     */\n    public final void addEntries(Iterator<Entry<?>> entries) {\n        if (entries != null) {\n            while (entries.hasNext()) {\n                addEntry(entries.next());\n            }\n        }\n    }\n\n    /**\n     * Adds the entries returned by the iterable to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to add\n     */\n    public final void addEntries(Iterable<Entry<?>> entries) {\n        if (entries != null) {\n            addEntries(entries.iterator());\n        }\n    }\n\n    // support for removing entries\n\n    /**\n     * Removes the given entry from the calendar. This is basically just a convenience\n     * method as the actual work of removing an entry from a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entry the entry to remove\n     */\n    public final void removeEntry(Entry<?> entry) {\n        removeEntries(entry);\n    }\n\n    /**\n     * Removes the given entries from the calendar. This is basically just a convenience\n     * method as the actual work of removing an entry from a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to remove\n     */\n    public final void removeEntries(Entry<?>... entries) {\n        if (entries != null) {\n            for (Entry<?> entry : entries) {\n                entry.setCalendar(null);\n            }\n        }\n    }\n\n    /**\n     * Removes the given entries from the calendar. This is basically just a convenience\n     * method as the actual work of removing an entry from a calendar is done inside\n     * {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the collection of entries to remove\n     */\n    public final void removeEntries(Collection<Entry<?>> entries) {\n        if (entries != null) {\n            entries.forEach(this::removeEntry);\n        }\n    }\n\n    /**\n     * Removes the entries returned by the iterator from the calendar. This is basically just a convenience\n     * method as the actual work of removing an entry from a calendar is done inside {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to remove\n     */\n    public final void removeEntries(Iterator<Entry<?>> entries) {\n        if (entries != null) {\n            while (entries.hasNext()) {\n                removeEntry(entries.next());\n            }\n        }\n    }\n\n    /**\n     * Adds the entries returned by the iterable to the calendar. This is basically just a convenience\n     * method as the actual work of adding an entry to a calendar is done inside {@link Entry#setCalendar(Calendar)}.\n     *\n     * @param entries the entries to add\n     */\n    public final void removeEntries(Iterable<Entry<?>> entries) {\n        if (entries != null) {\n            removeEntries(entries.iterator());\n        }\n    }\n\n    final void impl_addEntry(Entry<?> entry) {\n        if (entry.isRecurrence()) {\n            throw new IllegalArgumentException(\"a recurrence entry can not be added to a calendar\");\n        }\n\n        dirty = true;\n\n        intervalTree.add(entry);\n    }\n\n    final void impl_removeEntry(Entry<?> entry) {\n        if (entry.isRecurrence()) {\n            throw new IllegalArgumentException(\"a recurrence entry can not be added to a calendar\");\n        }\n\n        dirty = true;\n\n        intervalTree.remove(entry);\n    }\n\n    // Name support.\n\n    private final StringProperty name = new SimpleStringProperty(this, \"name\", \"Untitled\");\n\n    /**\n     * A property used to store the name of the calendar.\n     *\n     * @return the property used for storing the calendar name\n     */\n    public final StringProperty nameProperty() {\n        return name;\n    }\n\n    /**\n     * Sets the value of {@link #nameProperty()}.\n     *\n     * @param name the new name for the calendar\n     */\n    public final void setName(String name) {\n        nameProperty().set(name);\n    }\n\n    /**\n     * Returns the value of {@link #nameProperty()}.\n     *\n     * @return the name of the calendar\n     */\n    public final String getName() {\n        return nameProperty().get();\n    }\n\n    // Short name support.\n\n    private final StringProperty shortName = new SimpleStringProperty(this, \"shortName\", \"Unt.\");\n\n    /**\n     * A property used to store the short name of the calendar.\n     *\n     * @return the property used for storing the calendar short name\n     */\n    public final StringProperty shortNameProperty() {\n        return shortName;\n    }\n\n    /**\n     * Sets the value of {@link #shortNameProperty()}.\n     *\n     * @param name the new short name for the calendar\n     */\n    public final void setShortName(String name) {\n        shortNameProperty().set(name);\n    }\n\n    /**\n     * Returns the value of {@link #shortNameProperty()}.\n     *\n     * @return the short name of the calendar\n     */\n    public final String getShortName() {\n        return shortNameProperty().get();\n    }\n\n    // Style prefix support.\n\n    private final StringProperty style = new SimpleStringProperty(this, \"style\",\n            Style.STYLE1.name().toLowerCase());\n\n    /**\n     * A property used to store the visual style that will be used for the\n     * calendar in the UI. A style can be any arbitrary name. The style will be\n     * used as a prefix to find the styles in the stylesheet. For examples\n     * please search the standard framework stylesheet for the predefined styles\n     * \"style1-\", \"style2-\", etc.\n     *\n     * @return the visual calendar style\n     */\n    public final StringProperty styleProperty() {\n        return style;\n    }\n\n    /**\n     * Sets the value of {@link #styleProperty()} based on one of the predefined\n     * styles (see also the enum {@link Style}). The image below shows how the\n     * styles appear in the UI.\n     *\n     * <img src=\"doc-files/styles.png\" alt=\"Styles\">\n     *\n     * @param style the calendar style\n     */\n    public final void setStyle(Style style) {\n        MODEL.finer(getName() + \": setting style to: \" + style);\n        setStyle(style.name().toLowerCase());\n    }\n\n    /**\n     * Sets the value of {@link #styleProperty()}.\n     *\n     * @param stylePrefix the calendar style\n     */\n    public final void setStyle(String stylePrefix) {\n        requireNonNull(stylePrefix);\n        MODEL.finer(getName() + \": setting style to: \" + style);\n        styleProperty().set(stylePrefix);\n    }\n\n    /**\n     * Returns the value of {@link #styleProperty()}.\n     *\n     * @return the current calendar style\n     */\n    public final String getStyle() {\n        return styleProperty().get();\n    }\n\n    // Read only support.\n\n    private final BooleanProperty readOnly = new SimpleBooleanProperty(this, \"readOnly\", false);\n\n    /**\n     * A property used to control if the calendar is read-only or not.\n     *\n     * @return true if the calendar is read-only (default is false)\n     */\n    public final BooleanProperty readOnlyProperty() {\n        return readOnly;\n    }\n\n    /**\n     * Returns the value of {@link #readOnlyProperty()}.\n     *\n     * @return true if the calendar can not be edited by the user\n     */\n    public final boolean isReadOnly() {\n        return readOnlyProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #readOnlyProperty()}.\n     *\n     * @param readOnly the calendar can not be edited by the user if true\n     */\n    public final void setReadOnly(boolean readOnly) {\n        MODEL.finer(getName() + \": setting read only to: \" + readOnly);\n        readOnlyProperty().set(readOnly);\n    }\n\n    private final ObservableList<EventHandler<CalendarEvent>> eventHandlers = FXCollections.observableArrayList();\n\n    /**\n     * Adds an event handler for calendar events. Handlers will be called when\n     * an entry gets added, removed, changes, etc.\n     *\n     * @param l the event handler to add\n     */\n    public final void addEventHandler(EventHandler<CalendarEvent> l) {\n        if (l != null) {\n            if (MODEL.isLoggable(FINER)) {\n                MODEL.finer(getName() + \": adding event handler: \" + l);\n            }\n            eventHandlers.add(l);\n        }\n    }\n\n    /**\n     * Removes an event handler from the calendar.\n     *\n     * @param l the event handler to remove\n     */\n    public final void removeEventHandler(EventHandler<CalendarEvent> l) {\n        if (l != null) {\n            if (MODEL.isLoggable(FINER)) {\n                MODEL.finer(getName() + \": removing event handler: \" + l);\n            }\n            eventHandlers.remove(l);\n        }\n    }\n\n    private boolean fireEvents = true;\n\n    /**\n     * Fires the given calendar event to all event handlers currently registered\n     * with this calendar.\n     *\n     * @param evt the event to fire\n     */\n    public final void fireEvent(CalendarEvent evt) {\n        if (fireEvents && !batchUpdates) {\n            if (MODEL.isLoggable(FINER)) {\n                MODEL.finer(getName() + \": firing event: \" + evt);\n            }\n\n            requireNonNull(evt);\n            Event.fireEvent(this, evt);\n        }\n    }\n\n    @Override\n    public final EventDispatchChain buildEventDispatchChain(EventDispatchChain givenTail) {\n        return givenTail.append((event, tail) -> {\n            if (event instanceof CalendarEvent) {\n                for (EventHandler<CalendarEvent> handler : eventHandlers) {\n                    handler.handle((CalendarEvent) event);\n                }\n            }\n\n            return event;\n        });\n    }\n\n    private final ObjectProperty<T> userObject = new SimpleObjectProperty<>(this, \"userObject\");\n\n    public final T getUserObject() {\n        return userObject.get();\n    }\n\n    /**\n     * An (optional) user object that can be used to link this calendar to the source\n     * of its data or the business object that it represents.\n     *\n     * @return a user object\n     */\n    public final ObjectProperty<T> userObjectProperty() {\n        return userObject;\n    }\n\n    public final void setUserObject(T userObject) {\n        this.userObject.set(userObject);\n    }\n\n    @Override\n    public String toString() {\n        return \"Calendar [name=\" + getName() + \", style=\" + getStyle() + \", readOnly=\" + isReadOnly() + \", \" + (getUserObject() != null ? getUserObject().toString() : \"null\") + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/CalendarEvent.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport javafx.event.Event;\nimport javafx.event.EventType;\n\nimport java.time.ZonedDateTime;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * An event class used to signal changes done within a calendar or changes done\n * to a calendar entry. Events of this type can be received by adding an event\n * handler to a calendar.\n *\n *\n * <h2>Example</h2>\n *\n * <pre>\n * {@code\n * Calendar calendar = new Calendar(\"Home\");\n * calendar.addEventHandler(CalendarEvent.ENTRY_ADDED, evt -> {...});\n * }\n * </pre>\n *\n * @see Calendar#addEventHandler(javafx.event.EventHandler)\n */\npublic class CalendarEvent extends Event {\n\n    private static final long serialVersionUID = 4279597664476680474L;\n\n\n    /**\n     * The supertype of all event types in this event class.\n     */\n    public static final EventType<CalendarEvent> ANY = new EventType<>(Event.ANY, \"CALENDAR\");\n\n    /**\n     * An event type used to inform the application that \"something\" inside the\n     * calendar has changed and that the views need to update their visuals\n     * accordingly (brute force update).\n     */\n    public static final EventType<CalendarEvent> CALENDAR_CHANGED = new EventType<>(CalendarEvent.ANY, \"CALENDAR_CHANGED\");\n\n    /**\n     * The supertype of all events that a related to an entry itself and not the\n     * calendar.\n     */\n    public static final EventType<CalendarEvent> ENTRY_CHANGED = new EventType<>(CalendarEvent.ANY, \"ENTRY_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has been moved\n     * from one calendar to another.\n     */\n    public static final EventType<CalendarEvent> ENTRY_CALENDAR_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_CALENDAR_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has become a\n     * \"full day\" entry, meaning its start and end time are no longer relevant.\n     * The entry should be visualized in a way that signals that the entry will\n     * take all day (e.g. a birthday).\n     */\n    public static final EventType<CalendarEvent> ENTRY_FULL_DAY_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_FULL_DAY_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has been\n     * assigned a new user object.\n     */\n    public static final EventType<CalendarEvent> ENTRY_RECURRENCE_RULE_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_RECURRENCE_RULE_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has been\n     * assigned a new title.\n     */\n    public static final EventType<CalendarEvent> ENTRY_TITLE_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_TITLE_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has been\n     * assigned a new user object.\n     */\n    public static final EventType<CalendarEvent> ENTRY_USER_OBJECT_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_USER_OBJECT_CHANGED\");\n\n    /**\n     * An event type used to inform the application that an entry has been\n     * assigned a new user object.\n     */\n    public static final EventType<CalendarEvent> ENTRY_LOCATION_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_LOCATION_CHANGED\");\n\n    /**\n     * An event type used to inform the application that the time bounds of an\n     * entry have been changed. One or several of start / end date, start / end\n     * time.\n     */\n    public static final EventType<CalendarEvent> ENTRY_INTERVAL_CHANGED = new EventType<>(CalendarEvent.ENTRY_CHANGED, \"ENTRY_INTERVAL_CHANGED\");\n\n    private Entry<?> entry;\n\n    private final Calendar calendar;\n\n    private boolean oldFullDay;\n\n    private String oldText;\n\n    private Calendar oldCalendar;\n\n    private Interval oldInterval;\n\n    private Object oldUserObject;\n\n    /**\n     * Constructs a new event for subclass.\n     *\n     * @param eventType the event type\n     * @param calendar  the calendar where the event occurred.\n     */\n    protected CalendarEvent(EventType<? extends CalendarEvent> eventType, Calendar calendar) {\n        super(calendar, calendar, eventType);\n\n        this.calendar = requireNonNull(calendar);\n    }\n\n    /**\n     * Constructs a new event.\n     *\n     * @param eventType the event type\n     * @param calendar  the calendar where the event occured\n     * @param entry     the affected entry\n     */\n    public CalendarEvent(EventType<? extends CalendarEvent> eventType, Calendar calendar, Entry<?> entry) {\n        super(calendar, calendar, eventType);\n\n        this.calendar = calendar;\n        this.entry = requireNonNull(entry);\n    }\n\n    /**\n     * Constructs a new event used for signalling that an entry was assigned to\n     * a new calendar. The entry already carries a reference to new calendar and\n     * the event object will know the old calendar.\n     *\n     * @param eventType   the event type\n     * @param calendar    the calendar where the event occured\n     * @param entry       the affected entry\n     * @param oldCalendar the calendar to which the event belonged before\n     */\n    public CalendarEvent(EventType<CalendarEvent> eventType, Calendar calendar, Entry<?> entry, Calendar oldCalendar) {\n        this(eventType, calendar, entry);\n        this.oldCalendar = oldCalendar;\n    }\n\n    /**\n     * Constructs a new event used for signalling that an entry has been\n     * assigned a new user object. The entry already carries a reference to the\n     * new user object and the event object will know the old user object.\n     *\n     * @param eventType     the event type\n     * @param calendar      the calendar where the event occured\n     * @param entry         the affected entry\n     * @param oldUserObject the calendar to which the event belonged before\n     */\n    public CalendarEvent(EventType<CalendarEvent> eventType, Calendar calendar, Entry<?> entry, Object oldUserObject) {\n        this(eventType, calendar, entry);\n        this.oldUserObject = oldUserObject;\n    }\n\n    /**\n     * Constructs a new event used for signalling that an entry was assigned a\n     * new start end date / time. The entry already carries the new values,\n     * while the old values can be retrieved from the event object.\n     *\n     * @param eventType   the event type\n     * @param calendar    the calendar where the event occured\n     * @param entry       the affected entry\n     * @param oldInterval the previous time interval\n     */\n    public CalendarEvent(EventType<CalendarEvent> eventType, Calendar calendar, Entry<?> entry, Interval oldInterval) {\n        this(eventType, calendar, entry);\n        this.oldInterval = requireNonNull(oldInterval);\n    }\n\n    /**\n     * Constructs a new event used for signalling that an entry was assigned a\n     * new text (normally the title). The entry already carries a reference to\n     * new text and the event object will know the old one.\n     *\n     * @param eventType the event type\n     * @param calendar  the calendar where the event occured\n     * @param entry     the affected entry\n     * @param oldText   the previous value of the text\n     */\n    public CalendarEvent(EventType<CalendarEvent> eventType, Calendar calendar, Entry<?> entry, String oldText) {\n        this(eventType, calendar, entry);\n        this.oldText = oldText;\n    }\n\n    /**\n     * Constructs a new event used for signaling that an entry was set to full\n     * day. The entry already carries a reference to new full day value and the\n     * event object will know the old one.\n     *\n     * @param eventType  the event type\n     * @param calendar   the calendar where the event occured\n     * @param entry      the affected entry\n     * @param oldFullDay the previous value of the full day\n     */\n    public CalendarEvent(EventType<CalendarEvent> eventType, Calendar calendar, Entry<?> entry, boolean oldFullDay) {\n        this(eventType, calendar, entry);\n        this.oldFullDay = oldFullDay;\n    }\n\n    /**\n     * Returns the entry for which the event was fired.\n     *\n     * @return the affected entry\n     */\n    public Entry<?> getEntry() {\n        return entry;\n    }\n\n    /**\n     * Returns the calendar for which the event was fired.\n     *\n     * @return the affected calendar\n     */\n    public final Calendar getCalendar() {\n        return calendar;\n    }\n\n    /**\n     * Returns the old user object of the modified entry.\n     *\n     * @return the old user object\n     */\n    public Object getOldUserObject() {\n        return oldUserObject;\n    }\n\n    /**\n     * Returns the old time interval of the modified entry.\n     *\n     * @return the old time interval\n     */\n    public Interval getOldInterval() {\n        return oldInterval;\n    }\n\n    /**\n     * Returns the old text.\n     *\n     * @return the old text\n     */\n    public final String getOldText() {\n        return oldText;\n    }\n\n    /**\n     * Returns the old value of the \"full day\" flag.\n     *\n     * @return the old value of \"full day\"\n     */\n    public final boolean getOldFullDay() {\n        return oldFullDay;\n    }\n\n    /**\n     * Returns the old calendar.\n     *\n     * @return the old calendar\n     */\n    public final Calendar getOldCalendar() {\n        return oldCalendar;\n    }\n\n    /**\n     * A utility method to determine if the event describes the creation of a new\n     * entry. This is the case when the event type is {@link #ENTRY_CALENDAR_CHANGED} and\n     * the old calendar was null.\n     *\n     * @return true if the event describes the creation of a new entry\n     */\n    public final boolean isEntryAdded() {\n        if (eventType == ENTRY_CALENDAR_CHANGED) {\n            return (getOldCalendar() == null && entry.getCalendar() != null);\n        }\n\n        return false;\n    }\n\n    /**\n     * A utility method to determine if the event describes the removal of an\n     * entry. This is the case when the event type is {@link #ENTRY_CALENDAR_CHANGED} and\n     * the old calendar was not null but the new calendar is null.\n     *\n     * @return true if the event describes the removal of a new entry\n     */\n    public final boolean isEntryRemoved() {\n        if (eventType == ENTRY_CALENDAR_CHANGED) {\n            return (getOldCalendar() != null && entry.getCalendar() == null);\n        }\n\n        return false;\n    }\n\n    /**\n     * Determines whether the event will have an impact on different days.\n     * The method will return false if the user simply changed the start and / or\n     * end time of an entry within the current day. However, when the user drags\n     * the entry from one day to another then this method will return true.\n     *\n     * @return true if the new time interval of the entry touches other days than the old time interval of the entry\n     */\n    public final boolean isDayChange() {\n        if (!getEventType().equals(ENTRY_INTERVAL_CHANGED)) {\n            return false;\n        }\n\n        Interval newInterval = entry.getInterval();\n        Interval oldInterval = getOldInterval();\n\n        ZonedDateTime newStart = newInterval.getStartZonedDateTime();\n        ZonedDateTime oldStart = oldInterval.getStartZonedDateTime();\n        if (!newStart.toLocalDate().equals(oldStart.toLocalDate())) {\n            return true;\n        }\n\n        ZonedDateTime newEnd = newInterval.getEndZonedDateTime();\n        ZonedDateTime oldEnd = oldInterval.getEndZonedDateTime();\n        return !newEnd.toLocalDate().equals(oldEnd.toLocalDate());\n\n    }\n\n    @Override\n    public String toString() {\n        return \"CalendarEvent [\"\n                + (entry == null ? \"\" : (\"entry=\" + entry + \", \")) + \"calendar=\"\n                + calendar + \", oldInterval=\" + oldInterval + \", oldFullDay=\"\n                + oldFullDay + \", oldText=\" + oldText + \", oldCalendar=\"\n                + oldCalendar + \", eventType=\" + eventType + \", target=\"\n                + target + \", consumed=\" + consumed + \", source=\" + source\n                + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/CalendarSource.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.SourceView;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener.Change;\nimport javafx.collections.ObservableList;\n\nimport static com.calendarfx.util.LoggingDomain.MODEL;\nimport static java.util.logging.Level.FINE;\n\n/**\n * A calendar source is a collection of calendars. It often represents a user\n * account with some calendar service, e.g. Google Calendar or Apple me.com. The\n * image below shows an example: a calendar source called \"Work\" with calendars\n * \"Meetings, Training, Customers, Holidays\".\n *\n *\n * <img src=\"doc-files/calendar-source.png\" alt=\"Calendar Source View\">\n * <p>\n * <p>\n * Calendar sources can be shown to the user via the {@link SourceView} control.\n */\npublic class CalendarSource {\n\n    /**\n     * Constructs a new untitled calendar source.\n     */\n    public CalendarSource() {\n        if (MODEL.isLoggable(FINE)) {\n\n            getCalendars().addListener((Change<? extends Calendar> change) -> {\n                while (change.next()) {\n                    if (change.wasAdded()) {\n                        for (Calendar calendar : change.getAddedSubList()) {\n                            LoggingDomain.MODEL.fine(\"added calendar \" + calendar.getName() + \" to source \" + getName());\n                        }\n                    } else if (change.wasRemoved()) {\n                        for (Calendar calendar : change.getRemoved()) {\n                            MODEL.fine(\"removed calendar \" + calendar.getName() + \" from source \" + getName());\n                        }\n                    }\n                }\n            });\n\n        }\n    }\n\n    /**\n     * Constructs a new calendar source with the given name.\n     *\n     * @param name the name of the calendar source, e.g. \"Google\", \"Apple\"\n     */\n    public CalendarSource(String name) {\n        this();\n        setName(name);\n    }\n\n    private final StringProperty name = new SimpleStringProperty(this, \"name\", \"Untitled\");\n\n    /**\n     * The property used to store the name of the calendar source.\n     *\n     * @return the name property\n     */\n    public final StringProperty nameProperty() {\n        return name;\n    }\n\n    /**\n     * Sets the value of {@link #nameProperty()}.\n     *\n     * @param name the new name for the calendar source\n     */\n    public final void setName(String name) {\n        MODEL.fine(\"changing name to \" + name);\n        nameProperty().set(name);\n    }\n\n    /**\n     * Returns the value fo {@link #nameProperty()}.\n     *\n     * @return the calendar source name\n     */\n    public final String getName() {\n        return nameProperty().get();\n    }\n\n    private final ObservableList<Calendar> calendars = FXCollections.observableArrayList();\n\n    /**\n     * Returns the list of calendars that belong to this calendar source.\n     * Example: the calendar source is \"Google\" and calendars might be \"Work\",\n     * \"Home\", \"Sport\", \"Children\".\n     *\n     * @return the calendars owned by this calendar source\n     */\n    public final ObservableList<Calendar> getCalendars() {\n        return calendars;\n    }\n\n    @Override\n    public String toString() {\n        return \"CalendarSource [name=\" + getName() + \", calendars=\" + calendars + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/Entry.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyBooleanProperty;\nimport javafx.beans.property.ReadOnlyBooleanWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.ReadOnlyStringProperty;\nimport javafx.beans.property.ReadOnlyStringWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.MapChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport net.fortuna.ical4j.model.Recur;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeParseException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.UUID;\n\nimport static com.calendarfx.util.LoggingDomain.MODEL;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.logging.Level.FINE;\n\n/**\n * An entry inside a calendar, for example \"Dentist Appointment, Feb 2nd, 9am\".\n * Entries are added to and managed by calendars. The main attributes of an\n * entry are:\n *\n * <ul>\n * <li><b>Title</b> - the title shown to the user in the UI</li>\n * <li><b>Interval</b> - the time interval and time zone occupied by the entry</li>\n * <li><b>Full Day</b> - a flag signalling whether the entry should be treated as a \"full day\" event, e.g. a birthday</li>\n * <li><b>Calendar</b> - the calendar to which the entry belongs</li>\n * </ul>\n * The default minimum duration of an entry is 15 minutes.\n *\n * <h2>Visual Appearance</h2>\n * The image below shows an entry called \"dentist appointment\" as it would be visualized\n * via an {@link com.calendarfx.view.DayEntryView} inside a {@link com.calendarfx.view.DayView}.\n *\n * <img src=\"doc-files/entry.png\" alt=\"Entry\">\n *\n *\n * <h2>Recurrence</h2>\n * <p>\n * This class supports the industry standard for defining recurring events (RFC\n * 2445). For recurring events the method {@link #setRecurrenceRule(String)}\n * must be fed with a valid RRULE string, for example \"RRULE:FREQ=DAILY\" for an\n * event that occures every day. The calendar to which the entry belongs is then\n * responsible for creating the recurring entries when its\n * {@link Calendar#findEntries(LocalDate, LocalDate, ZoneId)} method gets\n * invoked. Recurring entries will return \"true\" when their\n * {@link #isRecurrence()} method is called and they will also be able to return\n * the \"source\" entry ({@link #getRecurrenceSourceEntry()}).\n *\n * <h2>Example</h2>\n *\n * <pre>\n * Entry entry = new Entry(&quot;Dentist Appointment&quot;);\n * Interval interval = new Interval(...);\n * entry.setInterval(interval);\n * entry.setRecurrenceRule(\"RRULE:FREQ=DAILY;INTERVAL=2;\");\n *\n * Calendar calendar = new Calendar(&quot;Health&quot;);\n * calendar.addEntry(entry);\n * </pre>\n *\n * @param <T> the type of the user object\n */\npublic class Entry<T> implements Comparable<Entry<?>> {\n\n    private static final Duration DEFAULT_MINIMUM_DURATION = Duration.ofMinutes(15);\n\n    private String id;\n\n    /**\n     * Constructs a new entry with a default time interval. The ID will be generated\n     * via {@link UUID#randomUUID()}.\n     */\n    public Entry() {\n        this(UUID.randomUUID().toString());\n    }\n\n    /**\n     * Constructs a new entry with the given title and a default time interval.\n     * The ID will be generated via {@link UUID#randomUUID()}.\n     *\n     * @param title the title shown to the user\n     */\n    public Entry(String title) {\n        this(title, new Interval(), UUID.randomUUID().toString());\n    }\n\n    /**\n     * Constructs a new entry with the given title, a default time interval, and\n     * the given ID.\n     *\n     * @param title the title shown to the user\n     * @param id the unique id of the entry\n     */\n    public Entry(String title, String id) {\n        this(title, new Interval(), id);\n    }\n\n    /**\n     * Constructs a new entry with the given title. The ID will be generated\n     * via {@link UUID#randomUUID()}.\n     *\n     * @param title    the title shown to the user\n     * @param interval the time interval where the entry is located\n     */\n    public Entry(String title, Interval interval) {\n        this(title, interval, UUID.randomUUID().toString());\n    }\n\n    /**\n     * Constructs a new entry with the given title.\n     *\n     * @param title    the title shown to the user\n     * @param interval the time interval where the entry is located\n     * @param id       a unique ID, e.g. UUID.randomUUID();\n     */\n    public Entry(String title, Interval interval, String id) {\n        requireNonNull(title);\n        requireNonNull(interval);\n        requireNonNull(id);\n\n        setTitle(title);\n        setInterval(interval);\n        this.id = id;\n    }\n\n    // A map containing a set of properties for this entry\n    private ObservableMap<Object, Object> properties;\n\n    /**\n     * Returns an observable map of properties on this entry for use primarily\n     * by application developers.\n     *\n     * @return an observable map of properties on this entry for use primarily\n     * by application developers\n     */\n    public final ObservableMap<Object, Object> getProperties() {\n        if (properties == null) {\n            properties = FXCollections.observableMap(new HashMap<>());\n\n            MapChangeListener<? super Object, ? super Object> changeListener = change -> {\n                if (change.getKey().equals(\"com.calendarfx.recurrence.source\")) {\n                    if (change.getValueAdded() != null) {\n                        @SuppressWarnings(\"unchecked\")\n                        Entry<T> source = (Entry<T>) change.getValueAdded();\n\n                        // lookup of property first to instantiate\n                        recurrenceSourceProperty();\n                        recurrenceSource.set(source);\n                    }\n                } else if (change.getKey().equals(\"com.calendarfx.recurrence.id\")) {\n                    if (change.getValueAdded() != null) {\n                        setRecurrenceId((String) change.getValueAdded());\n                    }\n                }\n            };\n\n            properties.addListener(changeListener);\n        }\n\n        return properties;\n    }\n\n    /**\n     * Tests if the entry has properties.\n     *\n     * @return true if the entry has properties.\n     */\n    public final boolean hasProperties() {\n        return properties != null && !properties.isEmpty();\n    }\n\n    // Lazily instantiated to save memory.\n    private ObservableList<String> styleClass;\n\n    /**\n     * Checks whether the entry has defined any custom styles at all. Calling\n     * this method is better than calling {@link #getStyleClass()} directly as\n     * it does not instantiate the lazily created style class list if it doesn't\n     * exist, yet.\n     *\n     * @return true if the entry defines any styles on its own\n     */\n    public final boolean hasStyleClass() {\n        return styleClass != null && !styleClass.isEmpty();\n    }\n\n    /**\n     * Returns a list of style classes. Adding styles to this list allows the\n     * application to style individual calendar entries (e.g. based on some kind\n     * of status).\n     *\n     * @return a list of style classes\n     */\n    public final ObservableList<String> getStyleClass() {\n        if (styleClass == null) {\n            styleClass = FXCollections.observableArrayList();\n        }\n        return styleClass;\n    }\n\n    private final ObjectProperty<Interval> interval = new SimpleObjectProperty<>(this, \"interval\") {\n        @Override\n        public void set(Interval newInterval) {\n\n            if (newInterval == null) {\n                return;\n            }\n\n            Interval oldInterval = getValue();\n\n            if (!Objects.equals(newInterval, oldInterval)) {\n\n                Calendar calendar = getCalendar();\n\n                if (!isRecurrence() && calendar != null) {\n                    calendar.impl_removeEntry(Entry.this);\n                }\n\n                super.set(newInterval);\n\n                /*\n                 * Update the read-only properties if needed.\n                 */\n                if (startDate != null) {\n                    startDate.set(newInterval.getStartDate());\n                }\n                if (startTime != null) {\n                    startTime.set(newInterval.getStartTime());\n                }\n                if (endDate != null) {\n                    endDate.set(newInterval.getEndDate());\n                }\n                if (endTime != null) {\n                    endTime.set(newInterval.getEndTime());\n                }\n                if (zoneId != null) {\n                    zoneId.set(newInterval.getZoneId());\n                }\n\n                updateMultiDay();\n\n                if (calendar != null) {\n\n                    if (!isRecurrence()) {\n                        calendar.impl_addEntry(Entry.this);\n                    }\n\n                    calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_INTERVAL_CHANGED, calendar, Entry.this, oldInterval));\n                }\n            }\n        }\n    };\n\n    /**\n     * A property used to store the time interval occupied by this entry. The\n     * interval object stores the start and end dates, the start and end times,\n     * and the time zone. Changes to this property will automatically update the\n     * read-only properties {@link #startDateProperty()},\n     * {@link #endDateProperty()}, {@link #startTimeProperty()},\n     * {@link #endTimeProperty()}, {@link #zoneIdProperty()}, and\n     * {@link #multiDayProperty()}.\n     *\n     * @return the time interval used by the entry\n     */\n    public final ObjectProperty<Interval> intervalProperty() {\n        return interval;\n    }\n\n    /**\n     * Returns the value of {@link #intervalProperty()}.\n     *\n     * @return the time interval used by the entry\n     */\n    public final Interval getInterval() {\n        return interval.get();\n    }\n\n    /**\n     * Sets the value of {@link #intervalProperty()}.\n     *\n     * @param interval the new time interval used by the entry\n     */\n    public final void setInterval(Interval interval) {\n        requireNonNull(interval);\n        intervalProperty().set(interval);\n    }\n\n    // Set Interval: LocalDate support\n\n    public final void setInterval(LocalDate date) {\n        setInterval(date, getZoneId());\n    }\n\n    public final void setInterval(LocalDate date, ZoneId zoneId) {\n        setInterval(date, date, zoneId);\n    }\n\n    public final void setInterval(LocalDate startDate, LocalDate endDate) {\n        setInterval(startDate, endDate, getZoneId());\n    }\n\n    public final void setInterval(LocalDate startDate, LocalDate endDate, ZoneId zoneId) {\n        setInterval(startDate, LocalTime.MIN, endDate, LocalTime.MAX, zoneId);\n    }\n\n    public final void setInterval(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) {\n        setInterval(startDate, startTime, endDate, endTime, getZoneId());\n    }\n\n    public final void setInterval(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime, ZoneId zoneId) {\n        setInterval(new Interval(startDate, startTime, endDate, endTime, zoneId));\n    }\n\n    // Set Interval: LocalTime support\n\n    public final void setInterval(LocalTime startTime, LocalTime endTime) {\n        setInterval(startTime, endTime, getZoneId());\n    }\n\n    public final void setInterval(LocalTime startTime, LocalTime endTime, ZoneId zoneId) {\n        setInterval(getStartDate(), startTime, getEndDate(), endTime, zoneId);\n    }\n\n    // Set Interval: LocalDateTime support\n\n    public final void setInterval(LocalDateTime dateTime) {\n        setInterval(dateTime, dateTime);\n    }\n\n    public final void setInterval(LocalDateTime dateTime, ZoneId zoneId) {\n        setInterval(dateTime, dateTime, zoneId);\n    }\n\n    public final void setInterval(LocalDateTime startDateTime, LocalDateTime endDateTime) {\n        setInterval(startDateTime, endDateTime, getZoneId());\n    }\n\n    public final void setInterval(LocalDateTime startDateTime, LocalDateTime endDateTime, ZoneId zoneId) {\n        setInterval(new Interval(startDateTime, endDateTime, zoneId));\n    }\n\n    // Set Interval: ZonedDateTime support\n\n    public final void setInterval(ZonedDateTime date) {\n        setInterval(date, date);\n    }\n\n    public final void setInterval(ZonedDateTime startDate, ZonedDateTime endDate) {\n        setInterval(new Interval(startDate, endDate));\n    }\n\n    /**\n     * Changes the start date of the entry interval and ensures that the entry's interval\n     * stays valid, which means that the start time will be before the end time and that the\n     * duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     *\n     * @param date the new start date\n     */\n    public final void changeStartDate(LocalDate date) {\n        changeStartDate(date, false);\n    }\n\n    /**\n     * Changes the start date of the entry interval.\n     *\n     * @param date         the new start date\n     * @param keepDuration if true then this method will also change the end date and time in such a way that the total duration\n     *                     of the entry will not change. If false then this method will ensure that the entry's interval\n     *                     stays valid, which means that the start time will be before the end time and that the\n     *                     duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     */\n    public final void changeStartDate(LocalDate date, boolean keepDuration) {\n        requireNonNull(date);\n\n        Interval interval = getInterval();\n\n        LocalDateTime newStartDateTime = getStartAsLocalDateTime().with(date);\n        LocalDateTime endDateTime = getEndAsLocalDateTime();\n\n        if (keepDuration) {\n            endDateTime = newStartDateTime.plus(getDuration());\n            setInterval(newStartDateTime, endDateTime, getZoneId());\n        } else {\n\n            /*\n             * We might have a problem if the new start time is AFTER the current end time.\n             */\n            if (newStartDateTime.isAfter(endDateTime)) {\n                interval = interval.withEndDateTime(newStartDateTime.plus(interval.getDuration()));\n            }\n\n            setInterval(interval.withStartDate(date));\n        }\n    }\n\n    /**\n     * Changes the start time of the entry interval and ensures that the entry's interval\n     * stays valid, which means that the start time will be before the end time and that the\n     * duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     *\n     * @param time the new start time\n     */\n    public final void changeStartTime(LocalTime time) {\n        changeStartTime(time, false);\n    }\n\n    /**\n     * Changes the start time of the entry interval.\n     *\n     * @param time         the new start time\n     * @param keepDuration if true then this method will also change the end time in such a way that the total duration\n     *                     of the entry will not change. If false then this method will ensure that the entry's interval\n     *                     stays valid, which means that the start time will be before the end time and that the\n     *                     duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     */\n    public final void changeStartTime(LocalTime time, boolean keepDuration) {\n        requireNonNull(time);\n\n        Interval interval = getInterval();\n\n        LocalDateTime newStartDateTime = getStartAsLocalDateTime().with(time);\n        LocalDateTime endDateTime = getEndAsLocalDateTime();\n\n        if (keepDuration) {\n            endDateTime = newStartDateTime.plus(getDuration());\n            setInterval(newStartDateTime, endDateTime);\n        } else {\n            /*\n             * We might have a problem if the new start time is AFTER the current end time.\n             */\n            if (newStartDateTime.isAfter(endDateTime.minus(getMinimumDuration()))) {\n                interval = interval.withEndDateTime(newStartDateTime.plus(getMinimumDuration()));\n            }\n\n            setInterval(interval.withStartTime(time));\n        }\n    }\n\n    /**\n     * Changes the end date of the entry interval and ensures that the entry's interval\n     * stays valid, which means that the start time will be before the end time and that the\n     * duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     *\n     * @param date the new end date\n     */\n    public final void changeEndDate(LocalDate date) {\n        changeEndDate(date, false);\n    }\n\n    /**\n     * Changes the end date of the entry interval.\n     *\n     * @param date         the new end date\n     * @param keepDuration if true then this method will also change the start date and time in such a way that the total duration\n     *                     of the entry will not change. If false then this method will ensure that the entry's interval\n     *                     stays valid, which means that the start time will be before the end time and that the\n     *                     duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     */\n    public final void changeEndDate(LocalDate date, boolean keepDuration) {\n        requireNonNull(date);\n\n        Interval interval = getInterval();\n\n        LocalDateTime newEndDateTime = getEndAsLocalDateTime().with(date);\n        LocalDateTime startDateTime = getStartAsLocalDateTime();\n\n        if (keepDuration) {\n            startDateTime = newEndDateTime.minus(getDuration());\n            setInterval(startDateTime, newEndDateTime, getZoneId());\n        } else {\n            /*\n             * We might have a problem if the new end time is BEFORE the current start time.\n             */\n            if (newEndDateTime.isBefore(startDateTime)) {\n                interval = interval.withStartDateTime(newEndDateTime.minus(interval.getDuration()));\n            }\n\n            setInterval(interval.withEndDate(date));\n        }\n    }\n\n    /**\n     * Changes the end time of the entry interval and ensures that the entry's interval\n     * stays valid, which means that the start time will be before the end time and that the\n     * duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     *\n     * @param time the new end time\n     */\n    public final void changeEndTime(LocalTime time) {\n        changeEndTime(time, false);\n    }\n\n    /**\n     * Changes the end time of the entry interval.\n     *\n     * @param time         the new end time\n     * @param keepDuration if true then this method will also change the start time in such a way that the total duration\n     *                     of the entry will not change. If false then this method will ensure that the entry's interval\n     *                     stays valid, which means that the start time will be before the end time and that the\n     *                     duration of the entry will be at least the duration defined by the {@link #minimumDurationProperty()}.\n     */\n    public final void changeEndTime(LocalTime time, boolean keepDuration) {\n        requireNonNull(time);\n\n        Interval interval = getInterval();\n\n        LocalDateTime newEndDateTime = getEndAsLocalDateTime().with(time);\n        LocalDateTime startDateTime = getStartAsLocalDateTime();\n\n        if (keepDuration) {\n            startDateTime = newEndDateTime.minus(getDuration());\n            setInterval(startDateTime, newEndDateTime, getZoneId());\n        } else {\n            /*\n             * We might have a problem if the new end time is BEFORE the current start time.\n             */\n            if (newEndDateTime.isBefore(startDateTime.plus(getMinimumDuration()))) {\n                interval = interval.withStartDateTime(newEndDateTime.minus(getMinimumDuration()));\n            }\n\n            setInterval(interval.withEndTime(time));\n        }\n    }\n\n    /**\n     * Changes the zone ID of the entry interval.\n     *\n     * @param zoneId the new zone\n     */\n    public final void changeZoneId(ZoneId zoneId) {\n        requireNonNull(zoneId);\n        setInterval(getInterval().withZoneId(zoneId));\n    }\n\n    private ReadOnlyObjectWrapper<Entry<T>> recurrenceSource;\n\n    /**\n     * If the entry is a recurrence (see {@link #recurrenceProperty()}) then\n     * this property will store a reference to the entry for which the\n     * recurrence was created.\n     *\n     * @return the entry that was the source of the recurrence\n     */\n    public final ReadOnlyObjectProperty<Entry<T>> recurrenceSourceProperty() {\n        if (recurrenceSource == null) {\n            recurrenceSource = new ReadOnlyObjectWrapper<Entry<T>>(this, \"recurrenceSource\") {\n                @Override\n                public void set(Entry<T> newEntry) {\n                    super.set(newEntry);\n                    setRecurrence(newEntry != null);\n                }\n            };\n        }\n\n        return recurrenceSource.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceSourceProperty()}.\n     *\n     * @return the recurrence source\n     */\n    public final Entry<T> getRecurrenceSourceEntry() {\n        return recurrenceSource == null ? null : recurrenceSource.get();\n    }\n\n    /**\n     * If the entry defines a recurrence rule (see\n     * {@link #recurrenceRuleProperty()}) then the calendar will use this method\n     * to create one or more \"copies\" of the entry. The default implementation\n     * of this method will simply create a new instance of type {@link Entry}.\n     * The initialization of the standard fields (e.g. \"Interval\" or \"Title\") of\n     * the recurrence copy will be done by the calendar. <b>Subclasses should\n     * override this method to also initialize additional fields.</b>\n     *\n     * @return a recurrence \"copy\" of the entry.\n     */\n    public Entry<T> createRecurrence() {\n        return new Entry<>();\n    }\n\n    private boolean _recurrence;\n\n    private ReadOnlyBooleanWrapper recurrence;\n\n    /**\n     * A read-only property used to indicate whether the entry is a recurrence\n     * copy of a recurrence source. This property will be set to true if the\n     * property {@link #recurrenceSourceProperty()} gets initialized with a\n     * value other than null.\n     *\n     * @return true if the entry is a recurrence copy\n     * @see #recurrenceRuleProperty()\n     * @see #recurrenceSourceProperty()\n     */\n    public final ReadOnlyBooleanProperty recurrenceProperty() {\n        if (recurrence == null) {\n            recurrence = new ReadOnlyBooleanWrapper(this, \"recurrence\", _recurrence);\n        }\n        return recurrence.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceProperty()}.\n     *\n     * @return true if the entry is a recurrence copy\n     */\n    public final boolean isRecurrence() {\n        return recurrence == null ? _recurrence : recurrence.get();\n    }\n\n    private void setRecurrence(boolean b) {\n        if (recurrence == null) {\n            _recurrence = b;\n        } else {\n            recurrence.set(b);\n        }\n    }\n\n    /**\n     * Determines if the entry describes a recurring event.\n     *\n     * @return true if the entry is recurring\n     * @see #recurrenceRuleProperty()\n     */\n    public final boolean isRecurring() {\n        return recurrenceRule != null && !(recurrenceRule.get() == null) && !recurrenceRule.get().isBlank();\n    }\n\n    /*\n     * Recurrence support.\n     */\n\n    private StringProperty recurrenceRule;\n\n    /**\n     * A property used to store a recurrence rule according to RFC-2445.\n     * <h3>Example</h3> Repeat entry / event every other day until September\n     * 1st, 2015.\n     *\n     * <pre>\n     * String rrule = \"RRULE:FREQ=DAILY;INTERVAL=2;UNTIL=20150901\";\n     * setRecurrenceRule(rrule);\n     * </pre>\n     *\n     * @return the recurrenceRule property\n     * @see #recurrenceEndProperty()\n     */\n    public final StringProperty recurrenceRuleProperty() {\n        if (recurrenceRule == null) {\n            recurrenceRule = new SimpleStringProperty(null, \"recurrenceRule\") {\n                @Override\n                public void set(String newRecurrence) {\n                    String oldRecurrence = get();\n\n                    if (!Objects.equals(oldRecurrence, newRecurrence)) {\n\n                        Calendar calendar = getCalendar();\n\n                        if (calendar != null && !isRecurrence()) {\n                            calendar.impl_removeEntry(Entry.this);\n                        }\n\n                        super.set(newRecurrence);\n\n                        updateRecurrenceEndProperty(newRecurrence);\n\n                        if (calendar != null) {\n                            if (!isRecurrence()) {\n                                calendar.impl_addEntry(Entry.this);\n                            }\n                            calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_RECURRENCE_RULE_CHANGED, calendar, Entry.this, oldRecurrence));\n                        }\n                    }\n                }\n\n                private void updateRecurrenceEndProperty(String newRecurrence) {\n                    if (newRecurrence != null && !newRecurrence.trim().equals(\"\")) {\n                        try {\n                            Recur<LocalDate> recur = new Recur<>(newRecurrence.replaceFirst(\"^RRULE:\", \"\"));\n                            setRecurrenceEnd(Objects.requireNonNullElse(recur.getUntil(), LocalDate.MAX));\n                        } catch (IllegalArgumentException |\n                                 DateTimeParseException e) {\n                            e.printStackTrace();\n                        }\n                    } else {\n                        setRecurrenceEnd(LocalDate.MAX);\n                    }\n                }\n            };\n        }\n\n        return recurrenceRule;\n    }\n\n    /**\n     * Sets the value of {@link #recurrenceRuleProperty()}.\n     *\n     * @param rec the new recurrence rule\n     */\n    public final void setRecurrenceRule(String rec) {\n        if (recurrenceRule == null && rec == null) {\n            // no unnecessary property creation if everything is null\n            return;\n        }\n        recurrenceRuleProperty().set(rec);\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceRuleProperty()}.\n     *\n     * @return the recurrence rule\n     */\n    public final String getRecurrenceRule() {\n        return recurrenceRule == null ? null : recurrenceRule.get();\n    }\n\n    private String _recurrenceId;\n\n    private ReadOnlyStringWrapper recurrenceId;\n\n    /**\n     * Stores the recurrence ID which is being generated on-the-fly by the\n     * {@link Calendar} to which the recurrence source entry belongs.\n     *\n     * @return the recurrence ID property\n     */\n    public final ReadOnlyStringProperty recurrenceIdProperty() {\n        if (recurrenceId == null) {\n            recurrenceId = new ReadOnlyStringWrapper(this, \"recurrenceId\", _recurrenceId);\n        }\n\n        return recurrenceId.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceIdProperty()}.\n     *\n     * @return the recurrence ID\n     */\n    public final String getRecurrenceId() {\n        return recurrenceId == null ? _recurrenceId : recurrenceId.get();\n    }\n\n    private void setRecurrenceId(String id) {\n        if (recurrenceId == null) {\n            _recurrenceId = id;\n        } else {\n            recurrenceId.set(id);\n        }\n    }\n\n    private LocalDate _recurrenceEnd;\n\n    private ReadOnlyObjectWrapper<LocalDate> recurrenceEnd;\n\n    /**\n     * The property used to store the end time of the recurrence rule.\n     *\n     * @return the recurrence rule end time\n     * @see #recurrenceRuleProperty()\n     */\n    public final ReadOnlyObjectProperty<LocalDate> recurrenceEndProperty() {\n        if (recurrenceEnd == null) {\n            recurrenceEnd = new ReadOnlyObjectWrapper<>(this, \"recurrenceEnd\", _recurrenceEnd);\n        }\n\n        return recurrenceEnd.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceRuleProperty()}.\n     *\n     * @return the recurrence rule end time\n     */\n    public final LocalDate getRecurrenceEnd() {\n        return recurrenceEnd == null ? _recurrenceEnd : recurrenceEnd.get();\n    }\n\n    private void setRecurrenceEnd(LocalDate date) {\n        if (recurrenceEnd == null) {\n            _recurrenceEnd = date;\n        } else {\n            recurrenceEnd.set(date);\n        }\n    }\n\n    /**\n     * Assigns a new ID to the entry. IDs do not have to be unique. If several\n     * entries share the same ID it means that they are representing the same\n     * \"real world\" entry. An entry spanning multiple days will be shown via\n     * several entries in the month view. Clicking on one of them will select\n     * all of them as they all represent the same thing.\n     *\n     * @param id the new ID of the entry\n     */\n    public final void setId(String id) {\n        requireNonNull(id);\n        if (MODEL.isLoggable(FINE)) {\n            MODEL.fine(\"setting id to \" + id);\n        }\n        this.id = id;\n    }\n\n    /**\n     * Returns the ID of the entry.\n     *\n     * @return the id object\n     */\n    public final String getId() {\n        return id;\n    }\n\n    /*\n     * Calendar support.\n     */\n    private final SimpleObjectProperty<Calendar> calendar = new SimpleObjectProperty<Calendar>(this, \"calendar\") {\n\n        @Override\n        public void set(Calendar newCalendar) {\n            Calendar oldCalendar = get();\n\n            if (!Objects.equals(oldCalendar, newCalendar)) {\n\n                if (oldCalendar != null) {\n                    if (!isRecurrence()) {\n                        oldCalendar.impl_removeEntry(Entry.this);\n                    }\n                }\n\n                super.set(newCalendar);\n\n                if (newCalendar != null) {\n                    if (!isRecurrence()) {\n                        newCalendar.impl_addEntry(Entry.this);\n                    }\n                }\n\n                if (newCalendar != null) {\n                    newCalendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_CALENDAR_CHANGED, newCalendar, Entry.this, oldCalendar));\n                } else if (oldCalendar != null) {\n                    oldCalendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_CALENDAR_CHANGED, newCalendar, Entry.this, oldCalendar));\n                }\n            }\n        }\n    };\n\n    /**\n     * A property used to store a reference to the calendar that owns the entry.\n     *\n     * @return the calendar property\n     */\n    public final ObjectProperty<Calendar> calendarProperty() {\n        return calendar;\n    }\n\n    /**\n     * Sets the value of {@link #calendarProperty()}.\n     *\n     * @param cal the new owning calendar of this entry\n     */\n    public final void setCalendar(Calendar cal) {\n        calendar.set(cal);\n    }\n\n    /**\n     * Returns the value of {@link #calendarProperty()}.\n     *\n     * @return the owning calendar of this entry\n     */\n    public final Calendar getCalendar() {\n        return calendar.get();\n    }\n\n    /**\n     * A convenience method to easily remove the entry from its calendar. Delegates\n     * to {@link #setCalendar(Calendar)} and passes a null value.\n     */\n    public final void removeFromCalendar() {\n        setCalendar(null);\n    }\n\n    /*\n     * User object support.\n     */\n\n    private ObjectProperty<T> userObject;\n\n    /**\n     * A property used to store a reference to an optional user object. The user\n     * object is usually the reason why the entry was created.\n     *\n     * @return the user object property\n     */\n    public final ObjectProperty<T> userObjectProperty() {\n        if (userObject == null) {\n            userObject = new SimpleObjectProperty<T>(this, \"userObject\") {\n                @Override\n                public void set(T newObject) {\n                    T oldUserObject = get();\n\n                    // We do not use .equals() here to allow to reset the object even if is \"looks\" the same e.g. if it\n                    // has some .equals() method implemented which just compares an id/business key.\n                    if (oldUserObject != newObject) {\n                        super.set(newObject);\n\n                        Calendar calendar = getCalendar();\n                        if (calendar != null) {\n                            calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_USER_OBJECT_CHANGED, calendar, Entry.this, oldUserObject));\n                        }\n                    }\n                }\n            };\n        }\n\n        return userObject;\n    }\n\n    /**\n     * Sets the value of {@link #userObjectProperty()}.\n     *\n     * @param object the new user object\n     */\n    public final void setUserObject(T object) {\n        if (userObject == null && object == null) {\n            // no unnecessary property creation if everything is null\n            return;\n        }\n        userObjectProperty().set(object);\n    }\n\n    /**\n     * Returns the value of {@link #userObjectProperty()}.\n     *\n     * @return the user object\n     */\n    public final T getUserObject() {\n        return userObject == null ? null : userObject.get();\n    }\n\n    /*\n     * Zone ID support.\n     */\n\n    private ReadOnlyObjectWrapper<ZoneId> zoneId;\n\n    /**\n     * A property used to store a time zone for the entry. The time zone is\n     * needed for properly interpreting the dates and times of the entry.\n     *\n     * @return the time zone property\n     */\n    public final ReadOnlyObjectProperty<ZoneId> zoneIdProperty() {\n        if (zoneId == null) {\n            zoneId = new ReadOnlyObjectWrapper<>(this, \"zoneId\", getInterval().getZoneId());\n        }\n\n        return zoneId.getReadOnlyProperty();\n    }\n\n    /**\n     * Sets the value of {@link #zoneIdProperty()}.\n     *\n     * @param zoneId the new time zone to use for this entry\n     */\n    public final void setZoneId(ZoneId zoneId) {\n        requireNonNull(zoneId);\n        setInterval(getInterval().withZoneId(zoneId));\n    }\n\n    /**\n     * Returns the value of {@link #zoneIdProperty()}.\n     *\n     * @return the entry's time zone\n     */\n    public final ZoneId getZoneId() {\n        return getInterval().getZoneId();\n    }\n\n    /*\n     * Title support.\n     */\n    private final StringProperty title = new SimpleStringProperty(this, \"title\") {\n        @Override\n        public void set(String newTitle) {\n            String oldTitle = get();\n\n            if (!Objects.equals(oldTitle, newTitle)) {\n                super.set(newTitle);\n\n                Calendar calendar = getCalendar();\n                if (calendar != null) {\n                    calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_TITLE_CHANGED, calendar, Entry.this, oldTitle));\n                }\n            }\n        }\n    };\n\n    /**\n     * A property used to store the title of the entry.\n     *\n     * @return the title property\n     */\n    public final StringProperty titleProperty() {\n        return title;\n    }\n\n    /**\n     * Sets the value of {@link #titleProperty()}.\n     *\n     * @param title the title shown by the entry\n     */\n    public final void setTitle(String title) {\n        titleProperty().set(title);\n    }\n\n    /**\n     * Returns the value of {@link #titleProperty()}.\n     *\n     * @return the title of the entry\n     */\n    public final String getTitle() {\n        return titleProperty().get();\n    }\n\n    /*\n     * Location support.\n     */\n\n    private StringProperty location;\n\n    /**\n     * A property used to store a free-text location specification for the given\n     * entry. This could be as simple as \"New York\" or a full address as in\n     * \"128 Madison Avenue, New York, USA\".\n     *\n     * @return the location of the event specified by the entry\n     */\n    public final StringProperty locationProperty() {\n        if (location == null) {\n            location = new SimpleStringProperty(null, \"location\") {\n                @Override\n                public void set(String newLocation) {\n                    String oldLocation = get();\n\n                    if (!Objects.equals(oldLocation, newLocation)) {\n\n                        super.set(newLocation);\n\n                        Calendar calendar = getCalendar();\n                        if (calendar != null) {\n                            calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_LOCATION_CHANGED, calendar, Entry.this, oldLocation));\n                        }\n                    }\n                }\n            };\n        }\n\n        return location;\n    }\n\n    /**\n     * Sets the value of {@link #locationProperty()}.\n     *\n     * @param loc the new location\n     */\n    public final void setLocation(String loc) {\n        if (location == null && loc == null) {\n            // no unnecessary property creation if everything is null\n            return;\n        }\n        locationProperty().set(loc);\n    }\n\n    /**\n     * Returns the value of {@link #locationProperty()}.\n     *\n     * @return the location\n     */\n    public final String getLocation() {\n        return location == null ? null : location.get();\n    }\n\n    private ReadOnlyObjectWrapper<LocalDate> startDate;\n\n    /**\n     * A read-only property used for retrieving the start date of the entry. The\n     * property gets updated whenever the start date inside the entry interval\n     * changes (see {@link #intervalProperty()}).\n     *\n     * @return the start date of the entry\n     */\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        if (startDate == null) {\n            startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\", getInterval().getStartDate());\n        }\n        return startDate.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the start date of the entry's interval (see\n     * {@link #intervalProperty()}).\n     *\n     * @return the entry's start date\n     */\n    public final LocalDate getStartDate() {\n        return getInterval().getStartDate();\n    }\n\n    private ReadOnlyObjectWrapper<LocalTime> startTime;\n\n    /**\n     * A read-only property used for retrieving the start time of the entry. The\n     * property gets updated whenever the start time inside the entry interval\n     * changes (see {@link #intervalProperty()}).\n     *\n     * @return the start time of the entry\n     */\n    public final ReadOnlyObjectProperty<LocalTime> startTimeProperty() {\n        if (startTime == null) {\n            startTime = new ReadOnlyObjectWrapper<>(this, \"startTime\", getInterval().getStartTime());\n        }\n        return startTime.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the start time of the entry's interval (see\n     * {@link #intervalProperty()}).\n     *\n     * @return the entry's start time\n     */\n    public final LocalTime getStartTime() {\n        return getInterval().getStartTime();\n    }\n\n    private ReadOnlyObjectWrapper<LocalDate> endDate;\n\n    /**\n     * A read-only property used for retrieving the end date of the entry. The\n     * property gets updated whenever the end date inside the entry interval\n     * changes (see {@link #intervalProperty()}).\n     *\n     * @return the end date of the entry\n     */\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        if (endDate == null) {\n            endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\", getInterval().getEndDate());\n        }\n\n        return endDate.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the end date of the entry's interval (see\n     * {@link #intervalProperty()}).\n     *\n     * @return the entry's end date\n     */\n    public final LocalDate getEndDate() {\n        return getInterval().getEndDate();\n    }\n\n    private ReadOnlyObjectWrapper<LocalTime> endTime;\n\n    /**\n     * A read-only property used for retrieving the end time of the entry. The\n     * property gets updated whenever the end time inside the entry interval\n     * changes (see {@link #intervalProperty()}).\n     *\n     * @return the end time of the entry\n     */\n    public final ReadOnlyObjectProperty<LocalTime> endTimeProperty() {\n        if (endTime == null) {\n            endTime = new ReadOnlyObjectWrapper<>(this, \"endTime\", getInterval().getEndTime());\n        }\n\n        return endTime.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the end time of the entry's interval (see\n     * {@link #intervalProperty()}).\n     *\n     * @return the entry's end time\n     */\n    public final LocalTime getEndTime() {\n        return getInterval().getEndTime();\n    }\n\n    /*\n     * Full day support.\n     */\n    private final BooleanProperty fullDay = new SimpleBooleanProperty(this, \"fullDay\", false) {\n\n        @Override\n        public void set(boolean newFullDay) {\n            boolean oldFullDay = get();\n\n            if (!Objects.equals(oldFullDay, newFullDay)) {\n\n                super.set(newFullDay);\n\n                Calendar calendar = getCalendar();\n                if (calendar != null) {\n                    calendar.fireEvent(new CalendarEvent(CalendarEvent.ENTRY_FULL_DAY_CHANGED, calendar, Entry.this));\n                }\n            }\n        }\n    };\n\n    /**\n     * A property used to signal whether an entry is considered to be a\n     * \"full day\" entry, for example a birthday. The image below shows how full\n     * day entries are shown in the UI.\n     *\n     * <img width=\"100%\" src=\"doc-files/full-day.png\" alt=\"Full Day\">\n     *\n     * @return the full day property\n     */\n    public final BooleanProperty fullDayProperty() {\n        return fullDay;\n    }\n\n    /**\n     * Returns the value of {@link #fullDayProperty()}.\n     *\n     * @return true if the entry is a full day entry, e.g. a birthday\n     */\n    public final boolean isFullDay() {\n        return fullDayProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #fullDayProperty()}.\n     *\n     * @param fullDay true if entry is a full day entry, e.g. a birthday\n     */\n    public final void setFullDay(boolean fullDay) {\n        fullDayProperty().set(fullDay);\n    }\n\n    // shadow field\n    private Duration _minimumDuration = DEFAULT_MINIMUM_DURATION;\n\n    private ObjectProperty<Duration> minimumDuration;\n\n    /**\n     * A property used to store the minimum duration an entry can have. It is\n     * often the case that applications do not allow calendar entries to be\n     * shorter than a certain duration. This property can be used to specify\n     * this. The default value is 15 minutes. Use {@link Duration#ZERO} to allow\n     * zero duration entries.\n     *\n     * @return the minimum duration of the entry\n     */\n    public final ObjectProperty<Duration> minimumDurationProperty() {\n        if (minimumDuration == null) {\n            minimumDuration = new SimpleObjectProperty<>(this, \"minimumDuration\", _minimumDuration);\n        }\n        return minimumDuration;\n    }\n\n    /**\n     * Returns the value of {@link #minimumDurationProperty()}.\n     *\n     * @return the minimum duration of the entry\n     */\n    public final Duration getMinimumDuration() {\n        return minimumDuration == null ? _minimumDuration : minimumDuration.get();\n    }\n\n    /**\n     * Sets the value of {@link #minimumDurationProperty()}.\n     *\n     * @param duration the minimum duration\n     */\n    public final void setMinimumDuration(Duration duration) {\n        Objects.requireNonNull(duration);\n        if (minimumDuration != null) {\n            minimumDuration.set(duration);\n        } else {\n            _minimumDuration = duration;\n        }\n    }\n\n    /*\n     * Utility methods.\n     */\n\n    /**\n     * Used by the {@link Calendar#findEntries(String)} to find entries based on\n     * a text search. This method can be overriden. The default implementation\n     * compares the given text with the title of the entry (lower case\n     * comparison).\n     *\n     * @param searchTerm the search term\n     * @return true if the entry matches the given search term\n     */\n    public boolean matches(String searchTerm) {\n        String title = getTitle();\n        if (title != null) {\n            return title.toLowerCase().contains(searchTerm.toLowerCase());\n        }\n\n        return false;\n    }\n\n    /**\n     * Utility method to get the zoned start time. This method combines the\n     * start date, start time, and the zone id to create a zoned date time\n     * object.\n     *\n     * @return the zoned start time\n     * @see #getStartDate()\n     * @see #getStartTime()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getStartAsZonedDateTime() {\n        return getInterval().getStartZonedDateTime();\n    }\n\n    /**\n     * Returns the start time in milliseconds since 1.1.1970.\n     *\n     * @return the start time in milliseconds\n     */\n    public final long getStartMillis() {\n        return getInterval().getStartMillis();\n    }\n\n    /**\n     * Utility method to get the local start date time. This method combines the\n     * start date and the start time to create a date time object.\n     *\n     * @return the start local date time\n     * @see #getStartDate()\n     * @see #getStartTime()\n     */\n    public final LocalDateTime getStartAsLocalDateTime() {\n        return getInterval().getStartDateTime();\n    }\n\n    /**\n     * Utility method to get the zoned end time. This method combines the end\n     * date, end time, and the zone id to create a zoned date time object.\n     *\n     * @return the zoned end time\n     * @see #getEndDate()\n     * @see #getEndTime()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getEndAsZonedDateTime() {\n        return getInterval().getEndZonedDateTime();\n    }\n\n    /**\n     * Returns the end time in milliseconds since 1.1.1970.\n     *\n     * @return the end time in milliseconds\n     */\n    public final long getEndMillis() {\n        return getInterval().getEndMillis();\n    }\n\n    /**\n     * Utility method to get the local end date time. This method combines the\n     * end date and the end time to create a date time object.\n     *\n     * @return the end local date time\n     * @see #getEndDate()\n     * @see #getEndTime()\n     */\n    public final LocalDateTime getEndAsLocalDateTime() {\n        return getInterval().getEndDateTime();\n    }\n\n    private void updateMultiDay() {\n        setMultiDay(getEndDate().isAfter(getStartDate()));\n    }\n\n    private boolean _multiDay;\n\n    private ReadOnlyBooleanWrapper multiDay;\n\n    /**\n     * A read-only property to determine if the entry spans several days. The\n     * image below shows such an entry.\n     *\n     * <img src=\"doc-files/multi-day.png\" alt=\"Multi Day\">\n     *\n     * @return true if the end date is after the start date (multiple days)\n     * @see #getStartDate()\n     * @see #getEndDate()\n     */\n    public final ReadOnlyBooleanProperty multiDayProperty() {\n        if (multiDay == null) {\n            multiDay = new ReadOnlyBooleanWrapper(this, \"multiDay\", _multiDay);\n        }\n        return multiDay.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #multiDayProperty()}.\n     *\n     * @return true if the entry spans multiple days\n     */\n    public final boolean isMultiDay() {\n        return multiDay == null ? _multiDay : multiDay.get();\n    }\n\n    private void setMultiDay(boolean b) {\n        if (multiDay == null) {\n            _multiDay = b;\n        } else {\n            multiDay.set(b);\n        }\n    }\n\n    /**\n     * Utility method to determine if this entry and the given entry intersect\n     * each other (time bounds overlap each other).\n     *\n     * @param entry the other entry to check\n     * @return true if the entries' time bounds overlap\n     */\n    public final boolean intersects(Entry<?> entry) {\n        return intersects(entry.getStartAsZonedDateTime(), entry.getEndAsZonedDateTime());\n    }\n\n    /**\n     * Utility method to determine if this entry and the given time interval\n     * intersect each other (time bounds overlap each other).\n     *\n     * @param startTime time interval start\n     * @param endTime   time interval end\n     * @return true if the entry and the given time interval overlap\n     */\n    public final boolean intersects(ZonedDateTime startTime, ZonedDateTime endTime) {\n        return Util.intersect(startTime, endTime, getStartAsZonedDateTime(), getEndAsZonedDateTime());\n    }\n\n    /**\n     * Utility method to calculate the duration of the entry. The duration is\n     * computed based on the zoned start and end time.\n     *\n     * @return the duration of the entry\n     * @see #getStartAsZonedDateTime()\n     * @see #getEndAsZonedDateTime()\n     */\n    public final Duration getDuration() {\n        return Duration.between(getStartAsZonedDateTime(), getEndAsZonedDateTime());\n    }\n\n    /**\n     * Checks whether the entry will be visible within the given start and end dates. This method\n     * takes recurrence into consideration and will return true if any recurrence of this entry\n     * will be displayed inside the given time interval.\n     *\n     * @param startDate the start date of the search interval\n     * @param endDate   the end date of the search interval\n     * @param zoneId    the time zone\n     * @return true if the entry or any of its recurrences is showing\n     */\n    public final boolean isShowing(LocalDate startDate, LocalDate endDate, ZoneId zoneId) {\n        return isShowing(this, startDate, endDate, zoneId);\n    }\n\n    private boolean isShowing(Entry<?> entry, LocalDate startDate, LocalDate endDate, ZoneId zoneId) {\n        ZonedDateTime st = ZonedDateTime.of(startDate, LocalTime.MIN, zoneId);\n        ZonedDateTime et = ZonedDateTime.of(endDate, LocalTime.MAX, zoneId);\n\n        if (entry.isRecurring() || entry.isRecurrence()) {\n            return isRecurrenceShowing(entry, st, et, zoneId);\n        }\n\n        Interval interval = entry.getInterval();\n\n        return Util.intersect(interval.getStartZonedDateTime(), interval.getEndZonedDateTime(), st, et);\n    }\n\n    private final BooleanProperty hidden = new SimpleBooleanProperty(this, \"hidden\", false);\n\n    public final boolean isHidden() {\n        return hidden.get();\n    }\n\n    public final BooleanProperty hiddenProperty() {\n        return hidden;\n    }\n\n    /**\n     * An entry can be made explicitly hidden.\n     *\n     * @param hidden true if the entry should not be visible in the calendar\n     */\n    public final void setHidden(boolean hidden) {\n        this.hidden.set(hidden);\n    }\n\n    private boolean isRecurrenceShowing(Entry<?> entry, ZonedDateTime st, ZonedDateTime et, ZoneId zoneId) {\n        String recurrenceRule = entry.getRecurrenceRule().replaceFirst(\"^RRULE:\", \"\");\n\n        LocalDate utilStartDate = entry.getStartDate();\n\n        try {\n            LocalDate utilEndDate = et.toLocalDate();\n\n            /*\n             * TODO: for performance reasons we should definitely\n             * use the advanceTo() call, but unfortunately this\n             * collides with the fact that e.g. the DetailedWeekView loads\n             * data day by day. So a given day would not show\n             * entries that start on the day before but intersect\n             * with the given day. We have to find a solution for\n             * this.\n             */\n            // iterator.advanceTo(st.toLocalDate());\n\n            List<LocalDate> dateList = new Recur<LocalDate>(recurrenceRule).getDates(utilStartDate, utilEndDate);\n\n            for (LocalDate repeatingDate : dateList) {\n                ZonedDateTime recurrenceStart = ZonedDateTime.of(repeatingDate, LocalTime.MIN, zoneId);\n                ZonedDateTime recurrenceEnd = recurrenceStart.plus(entry.getDuration());\n\n                if (Util.intersect(recurrenceStart, recurrenceEnd, st, et)) {\n                    return true;\n                }\n            }\n        } catch (IllegalArgumentException | DateTimeParseException ex) {\n            ex.printStackTrace();\n        }\n\n        return false;\n    }\n\n    @Override\n    public final int compareTo(Entry<?> other) {\n        if (isFullDay() && other.isFullDay()) {\n            return getStartDate().compareTo(other.getStartDate());\n        }\n\n        if (isFullDay()) {\n            return -1;\n        }\n\n        if (other.isFullDay()) {\n            return +1;\n        }\n\n        LocalDateTime a = LocalDateTime.of(getStartDate(), getStartTime());\n        LocalDateTime b = LocalDateTime.of(other.getStartDate(), other.getStartTime());\n        int result = a.compareTo(b);\n        if (result == 0) {\n            String titleA = getTitle() != null ? getTitle() : \"\";\n            String titleB = other.getTitle() != null ? other.getTitle() : \"\";\n            result = titleA.compareTo(titleB);\n        }\n\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"Entry [title=\" + getTitle() + \", id=\" + getId() + \", fullDay=\"\n                + isFullDay() + \", startDate=\" + getStartDate() + \", endDate=\"\n                + getEndDate() + \", startTime=\" + getStartTime() + \", endTime=\"\n                + getEndTime() + \", zoneId=\" + getZoneId() + \", recurring = \"\n                + isRecurring() + \", rrule = \" + getRecurrenceRule()\n                + \", recurrence = \" + isRecurrence() + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((id == null) ? 0 : id.hashCode());\n        result = prime * result + ((getRecurrenceId() == null) ? 0 : getRecurrenceId().hashCode());\n        return result;\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Entry other = (Entry) obj;\n        if (id == null) {\n            if (other.id != null) {\n                return false;\n            }\n        } else if (!id.equals(other.id)) {\n            return false;\n        }\n\n        String recId = getRecurrenceId();\n        String otherRecId = other.getRecurrenceId();\n\n        if (recId == null) {\n            return otherRecId == null;\n        }\n\n        return recId.equals(otherRecId);\n    }\n\n    private static final String ENTRY_CATEGORY = \"Entry\";\n\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = FXCollections.observableArrayList();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(calendarProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Calendar.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Calendar\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Calendar\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getCalendar();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setCalendar((Calendar) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(startTimeProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Start time\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Start time\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getStartTime();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                changeStartTime((LocalTime) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(endTimeProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"End time\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"End time\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getStartTime();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                changeEndTime((LocalTime) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(startDateProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Start date\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Start date\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getStartDate();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                changeStartDate((LocalDate) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(endDateProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"End date\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"End date\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getEndDate();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                changeEndDate((LocalDate) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(zoneIdProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return ZoneId.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Zone ID\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Zone ID\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getZoneId();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setZoneId((ZoneId) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(titleProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return String.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Title\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Title\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getTitle();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setTitle((String) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(fullDayProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Full Day\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Full Day\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isFullDay();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setFullDay((boolean) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(locationProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return String.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Location\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Geographic location (free text)\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getLocation();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setLocation((String) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(recurrenceRuleProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return String.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Recurrence Rule\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"RRULE\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getRecurrenceRule();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setRecurrenceRule((String) value);\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(minimumDurationProperty());\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Duration.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Minimum Duration\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Minimum Duration\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getMinimumDuration();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setMinimumDuration((Duration) value);\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/Interval.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A class used for storing the time interval and time zone of an entry.\n *\n * @see Entry#setInterval(Interval)\n */\npublic final class Interval {\n\n    private static final LocalDate defaultDate = LocalDate.now();\n\n    private static final LocalTime defaultStartTime = LocalTime.of(12, 0);\n\n    private static final LocalTime defaultEndTime = LocalTime.of(13, 0);\n\n    private static final ZoneId defaultZoneId = ZoneId.systemDefault();\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final LocalTime startTime;\n\n    private final LocalTime endTime;\n\n    private ZonedDateTime zonedStartDateTime;\n\n    private ZonedDateTime zonedEndDateTime;\n\n    private LocalDateTime startDateTime;\n\n    private LocalDateTime endDateTime;\n\n    private final ZoneId zoneId;\n\n    private long startMillis = Long.MIN_VALUE;\n\n    private long endMillis = Long.MAX_VALUE;\n\n    /**\n     * Constructs a new time interval with start and end dates equal to\n     * {@link LocalDate#now()}. The start and end times will be set to\n     * {@link LocalTime#now()} and {@link LocalTime#now()} plus one hour. The\n     * time zone will be set to {@link ZoneId#systemDefault()}.\n     */\n    public Interval() {\n        this(defaultDate, defaultStartTime, defaultDate, defaultEndTime, defaultZoneId);\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end dates /\n     * times. The time zone will be initialized with\n     * {@link ZoneId#systemDefault()}.\n     *\n     * @param startDate the start date (e.g. Oct. 3rd, 2015)\n     * @param startTime the start time (e.g. 10:45am)\n     * @param endDate   the end date\n     * @param endTime   the end time\n     */\n    public Interval(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) {\n        this(startDate, startTime, endDate, endTime, defaultZoneId);\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end dates /\n     * times. The time zone will be initialized with\n     * {@link ZoneId#systemDefault()}.\n     *\n     * @param startDateTime the start date and time (e.g. Oct. 3rd, 2015, 6:15pm)\n     * @param endDateTime   the end date and time\n     */\n    public Interval(LocalDateTime startDateTime, LocalDateTime endDateTime) {\n        this(startDateTime, endDateTime, ZoneId.systemDefault());\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end zoned dates /\n     * times. The time zone will be initialized with the time zone of the start\n     * date time. However, if the zone ID of the second argument is different then\n     * an exception will be thrown.\n     *\n     * @throws IllegalArgumentException if two different time zones are used\n     *\n     * @param zonedStartDateTime the start date and time (e.g. Oct. 3rd, 2015, 6:15pm)\n     * @param zonedEndDateTime   the end date and time\n     */\n    public Interval(ZonedDateTime zonedStartDateTime, ZonedDateTime zonedEndDateTime) {\n        this(zonedStartDateTime.toLocalDateTime(), zonedEndDateTime.toLocalDateTime(), zonedStartDateTime.getZone());\n\n        if (!zonedStartDateTime.getZone().equals(zonedEndDateTime.getZone())) {\n            throw new IllegalArgumentException(\"the zoned start and end times use different time zones, zone1 = \" + zonedStartDateTime.getZone() +\n                    \", zone2 = \" + zonedEndDateTime.getZone());\n        }\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end dates /\n     * times and time zone.\n     *\n     * @param startDateTime the start date and time (e.g. Oct. 3rd, 2015, 6:15pm)\n     * @param endDateTime   the end date and time\n     * @param zoneId        the time zone\n     */\n    public Interval(LocalDateTime startDateTime, LocalDateTime endDateTime, ZoneId zoneId) {\n        this(startDateTime.toLocalDate(), startDateTime.toLocalTime(), endDateTime.toLocalDate(), endDateTime.toLocalTime(), zoneId);\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end times and time zone.\n     *\n     * @param startTime the start time\n     * @param endTime   the end time\n     * @param zoneId    the time zone\n     */\n    public Interval(Instant startTime, Instant endTime, ZoneId zoneId) {\n        this(ZonedDateTime.ofInstant(startTime, zoneId), ZonedDateTime.ofInstant(endTime, zoneId));\n    }\n\n    /**\n     * Constructs a new time interval with the given start and end dates / times\n     * and time zone.\n     *\n     * @param startDate the start date (e.g. Oct. 3rd, 2015)\n     * @param startTime the start time (e.g. 10:45am)\n     * @param endDate   the end date\n     * @param endTime   the end time\n     * @param zoneId    the time zone\n     */\n    public Interval(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime, ZoneId zoneId) {\n        this.startDate = requireNonNull(startDate);\n        this.startTime = requireNonNull(startTime);\n        this.endDate = requireNonNull(endDate);\n        this.endTime = requireNonNull(endTime);\n        this.zoneId = requireNonNull(zoneId);\n\n        if (startDate.isAfter(endDate)) {\n            throw new IllegalArgumentException(\"the start date can never be after the end date\");\n        }\n\n        /*\n         * Now we know that the start date is either earlier than the end date or\n         * on the same date.\n         */\n\n        if (startDate.equals(endDate)) {\n\n            /*\n             * If the start date and the end date are on the same date then we have to make sure that the\n             * start time is not after the end time.\n             */\n            if (getStartTime().isAfter(getEndTime())) {\n                throw new IllegalArgumentException(\"the start time can not be after the end time if both are on the same date\");\n            }\n        }\n    }\n\n    /**\n     * Returns the time zone ID.\n     *\n     * @return the time zone\n     */\n    public ZoneId getZoneId() {\n        return zoneId;\n    }\n\n    /**\n     * Returns the start date of the interval.\n     *\n     * @return the start date\n     */\n    public LocalDate getStartDate() {\n        return startDate;\n    }\n\n    /**\n     * Returns the start time of the interval.\n     *\n     * @return the start time\n     */\n    public LocalTime getStartTime() {\n        return startTime;\n    }\n\n    /**\n     * A convenience method to retrieve a zoned date time based on the start\n     * date, start time, and time zone id.\n     *\n     * @return the zoned start time\n     */\n    public ZonedDateTime getStartZonedDateTime() {\n        if (zonedStartDateTime == null) {\n            zonedStartDateTime = ZonedDateTime.of(startDate, startTime, zoneId);\n        }\n\n        return zonedStartDateTime;\n    }\n\n    /**\n     * Returns the start time in milliseconds since 1.1.1970.\n     *\n     * @return the start time in milliseconds\n     */\n    public long getStartMillis() {\n        if (startMillis == Long.MIN_VALUE) {\n            startMillis = getStartZonedDateTime().toInstant().toEpochMilli();\n        }\n\n        return startMillis;\n    }\n\n    /**\n     * Returns the end date of the interval.\n     *\n     * @return the end date\n     */\n    public LocalDate getEndDate() {\n        return endDate;\n    }\n\n    /**\n     * Returns the end time of the interval.\n     *\n     * @return the end time\n     */\n    public LocalTime getEndTime() {\n        return endTime;\n    }\n\n    /**\n     * A convenience method to retrieve a zoned date time based on the end date,\n     * end time, and time zone id.\n     *\n     * @return the zoned end time\n     */\n    public ZonedDateTime getEndZonedDateTime() {\n        if (zonedEndDateTime == null) {\n            zonedEndDateTime = ZonedDateTime.of(endDate, endTime, zoneId);\n        }\n\n        return zonedEndDateTime;\n    }\n\n    /**\n     * Returns the start time in milliseconds since 1.1.1970.\n     *\n     * @return the start time in milliseconds\n     */\n    public long getEndMillis() {\n        if (endMillis == Long.MAX_VALUE) {\n            endMillis = getEndZonedDateTime().toInstant().toEpochMilli();\n        }\n\n        return endMillis;\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start\n     * and end date.\n     *\n     * @param startDate the new start date\n     * @param endDate the new end date\n     * @return a new interval\n     */\n    public Interval withDates(LocalDate startDate, LocalDate endDate) {\n        requireNonNull(startDate);\n        requireNonNull(endDate);\n        return new Interval(startDate, this.startTime, endDate, this.endTime, this.zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start\n     * and end date.\n     *\n     * @param startDateTime the new start date\n     * @param endDateTime the new end date\n     * @return a new interval\n     */\n    public Interval withDates(LocalDateTime startDateTime, LocalDateTime endDateTime) {\n        requireNonNull(startDateTime);\n        requireNonNull(endDateTime);\n        return new Interval(startDateTime, endDateTime, this.zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start\n     * and end time.\n     *\n     * @param startTime the new start time\n     * @param endTime the new end time\n     * @return a new interval\n     */\n    public Interval withTimes(LocalTime startTime, LocalTime endTime) {\n        requireNonNull(startTime);\n        requireNonNull(endTime);\n        return new Interval(this.startDate, startTime, this.endDate, endTime, this.zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start\n     * date.\n     *\n     * @param date the new start date\n     * @return a new interval\n     */\n    public Interval withStartDate(LocalDate date) {\n        requireNonNull(date);\n        return new Interval(date, startTime, endDate, endTime, zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different end\n     * date.\n     *\n     * @param date the new end date\n     * @return a new interval\n     */\n    public Interval withEndDate(LocalDate date) {\n        requireNonNull(date);\n        return new Interval(startDate, startTime, date, endTime, zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start\n     * time.\n     *\n     * @param time the new start time\n     * @return a new interval\n     */\n    public Interval withStartTime(LocalTime time) {\n        requireNonNull(time);\n        return new Interval(startDate, time, endDate, endTime, zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different start date\n     * and time.\n     *\n     * @param dateTime the new start date and time\n     * @return a new interval\n     */\n    public Interval withStartDateTime(LocalDateTime dateTime) {\n        requireNonNull(dateTime);\n        return new Interval(dateTime.toLocalDate(), dateTime.toLocalTime(), endDate, endTime);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different end\n     * time.\n     *\n     * @param time the new end time\n     * @return a new interval\n     */\n    public Interval withEndTime(LocalTime time) {\n        requireNonNull(time);\n        return new Interval(startDate, startTime, endDate, time, zoneId);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different end\n     * date and time.\n     *\n     * @param dateTime the new end date and time\n     * @return a new interval\n     */\n    public Interval withEndDateTime(LocalDateTime dateTime) {\n        requireNonNull(dateTime);\n        return new Interval(startDate, startTime, dateTime.toLocalDate(), dateTime.toLocalTime());\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different time\n     * zone id.\n     *\n     * @param zone the new time zone\n     * @return a new interval\n     */\n    public Interval withZoneId(ZoneId zone) {\n        requireNonNull(zone);\n        return new Interval(startDate, startTime, endDate, endTime, zone);\n    }\n\n    /**\n     * Returns a new interval based on this interval but with a different duration. The duration\n     * will change the end time and / or the end date.\n     *\n     * @param duration the new duration\n     * @return a new interval\n     */\n    public Interval withDuration(Duration duration) {\n        requireNonNull(duration);\n        ZonedDateTime zonedDateTime = ZonedDateTime.of(getStartDate(), getStartTime(), getZoneId()).plus(duration);\n        return new Interval(startDate, startTime, zonedDateTime.toLocalDate(), zonedDateTime.toLocalTime(), getZoneId());\n    }\n\n    /**\n     * Utility method to get the local start date time. This method combines the\n     * start date and the start time to create a date time object.\n     *\n     * @return the start local date time\n     * @see #getStartDate()\n     * @see #getStartTime()\n     */\n    public LocalDateTime getStartDateTime() {\n        if (startDateTime == null) {\n            startDateTime = LocalDateTime.of(getStartDate(), getStartTime());\n        }\n\n        return startDateTime;\n    }\n\n    /**\n     * Returns the duration of this interval.\n     *\n     * @return the duration between the zoned start and end date and time\n     */\n    public Duration getDuration() {\n        return Duration.between(getStartZonedDateTime(), getEndZonedDateTime());\n    }\n\n    /**\n     * Utility method to get the local end date time. This method combines the\n     * end date and the end time to create a date time object.\n     *\n     * @return the end local date time\n     * @see #getEndDate()\n     * @see #getEndTime()\n     */\n    public LocalDateTime getEndDateTime() {\n        if (endDateTime == null) {\n            endDateTime = LocalDateTime.of(getEndDate(), getEndTime());\n        }\n\n        return endDateTime;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((endDate == null) ? 0 : endDate.hashCode());\n        result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());\n        result = prime * result + ((startDate == null) ? 0 : startDate.hashCode());\n        result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());\n        result = prime * result + ((zoneId == null) ? 0 : zoneId.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        Interval other = (Interval) obj;\n        if (endDate == null) {\n            if (other.endDate != null)\n                return false;\n        } else if (!endDate.equals(other.endDate))\n            return false;\n        if (endTime == null) {\n            if (other.endTime != null)\n                return false;\n        } else if (!endTime.equals(other.endTime))\n            return false;\n        if (startDate == null) {\n            if (other.startDate != null)\n                return false;\n        } else if (!startDate.equals(other.startDate))\n            return false;\n        if (startTime == null) {\n            if (other.startTime != null)\n                return false;\n        } else if (!startTime.equals(other.startTime))\n            return false;\n        if (zoneId == null) {\n            return other.zoneId == null;\n        } else return zoneId.equals(other.zoneId);\n    }\n\n    @Override\n    public String toString() {\n        return \"Interval [startDate=\" + startDate + \", endDate=\" + endDate\n                + \", startTime=\" + startTime + \", endTime=\" + endTime\n                + \", zoneId=\" + zoneId + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/IntervalTree.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport java.time.Instant;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * An interval tree implementation to store entries based on their start and end\n * time.\n *\n * @param <E>\n *            the entry type\n */\nclass IntervalTree<E extends Entry<?>> {\n    // package private on purpose\n\n    private TreeEntry<E> root;\n    private int treeSize;\n    private final Set<String> entryIDs = new HashSet<>();\n\n    public final Instant getEarliestTimeUsed() {\n        if (root != null) {\n            return Instant.ofEpochMilli(getEarliestTimeUsed(root));\n        }\n\n        return null;\n    }\n\n    private long getEarliestTimeUsed(TreeEntry<E> entry) {\n        if (entry.getLeft() != null) {\n            return getEarliestTimeUsed(entry.getLeft());\n        }\n\n        return entry.low;\n    }\n\n    public final Instant getLatestTimeUsed() {\n        if (root != null) {\n            return Instant.ofEpochMilli(getLatestTimeUsed(root));\n        }\n\n        return null;\n    }\n\n    private long getLatestTimeUsed(TreeEntry<E> entry) {\n        if (entry.getRight() != null) {\n            return getLatestTimeUsed(entry.getRight());\n        }\n\n        return entry.high;\n    }\n\n    public final boolean add(E entry) {\n        TreeEntry<E> e = addEntry(entry);\n        return e != null;\n    }\n\n    /**\n     * Method to remove period/key object from tree. Entry to delete will be\n     * found by period and key values of given parameter p (not by given object\n     * reference).\n     *\n     * @param entry\n     *            the entry to remove\n     * @return true if the entry was a member of this tree\n     */\n    public final boolean remove(E entry) {\n        TreeEntry<E> e = getEntry(entry);\n\n        if (e == null) {\n            return false;\n        } else {\n            deleteEntry(e);\n        }\n\n        return true;\n    }\n\n    /**\n     * Method to determine if the interval tree contains the given entry.\n     *\n     * @param entry\n     *            the entry to check\n     * @return true if the entry is a member of this tree\n     */\n    public final boolean contains(E entry) {\n        TreeEntry<E> e = getEntry(entry);\n        return e != null;\n    }\n\n    public final Collection<E> removePeriod(Instant start, Instant end) {\n        Collection<E> result = getIntersectingObjects(start, end);\n\n        for (E p : result) {\n            deleteEntry(getEntry(p));\n        }\n\n        return result;\n    }\n\n    public final Collection<E> getIntersectingObjects(Instant start,\n                                                      Instant end) {\n        Collection<E> result = new ArrayList<>();\n        if (root == null) {\n            return result;\n        }\n\n        searchIntersecting(root, new TimeInterval(start, end), result);\n\n        return result;\n    }\n\n    private void searchIntersecting(TreeEntry<E> entry,\n                                    TimeInterval timeInterval, Collection<E> result) {\n        // Don't search nodes that don't exist\n        if (entry == null) {\n            return;\n        }\n\n        long pLow = getLow(timeInterval);\n        long pHigh = getHigh(timeInterval);\n\n        // If p is to the right of the rightmost point of any interval\n        // in this node and all children, there won't be any matches.\n        if (entry.maxHigh < pLow) {\n            return;\n        }\n\n        // Search left children\n        if (entry.left != null) {\n            searchIntersecting(entry.left, timeInterval, result);\n        }\n\n        // Check this node\n        if (checkPLow(entry, pLow) || checkPHigh(entry, pHigh)\n                || (pLow <= entry.low && entry.high <= pHigh)) {\n            result.add(entry.value);\n        }\n\n        // If p is to the left of the start of this interval,\n        // then it can't be in any child to the right.\n        if (pHigh < entry.low) {\n            return;\n        }\n\n        // Otherwise, search right children\n        if (entry.right != null) {\n            searchIntersecting(entry.right, timeInterval, result);\n        }\n    }\n\n    private boolean checkPLow(TreeEntry<E> n, long pLow) {\n        return n.low <= pLow && n.high > pLow;\n    }\n\n    private boolean checkPHigh(TreeEntry<E> n, long pHigh) {\n        return n.low < pHigh && n.high >= pHigh;\n    }\n\n    public final long size() {\n        return treeSize;\n    }\n\n    public final void clear() {\n        treeSize = 0;\n        root = null;\n    }\n\n    private long getLow(TimeInterval obj) {\n        try {\n            return obj.getStartTime() == null ? Long.MIN_VALUE\n                    : obj.getStartTime().toEpochMilli();\n        } catch (Exception e) {\n            return Long.MAX_VALUE;\n        }\n    }\n\n    private long getHigh(TimeInterval interval) {\n        try {\n            return interval.getEndTime() == null ? Long.MAX_VALUE\n                    : interval.getEndTime().toEpochMilli();\n        } catch (ArithmeticException e) {\n            return Long.MAX_VALUE;\n        }\n    }\n\n    private long getLow(Entry<?> entry) {\n        try {\n            return entry.getStartMillis();\n        } catch (ArithmeticException e) {\n            return Long.MAX_VALUE;\n        }\n    }\n\n    private long getHigh(Entry<?> entry) {\n        try {\n            return entry.isRecurring()\n                    ? ZonedDateTime.of(entry.getRecurrenceEnd(), LocalTime.MAX,\n                    entry.getZoneId()).toInstant().toEpochMilli()\n                    : entry.getEndMillis();\n        } catch (ArithmeticException e) {\n            return Long.MAX_VALUE;\n        }\n    }\n\n    private void fixUpMaxHigh(TreeEntry<E> entry) {\n        while (entry != null) {\n            entry.maxHigh = Math.max(entry.high,\n                    Math.max(entry.left != null ? entry.left.maxHigh\n                                    : Long.MIN_VALUE,\n                            entry.right != null ? entry.right.maxHigh\n                                    : Long.MIN_VALUE));\n            entry = entry.parent;\n        }\n    }\n\n    /**\n     * Method to find entry by period. Period start, period end and object key\n     * are used to identify each entry.\n     *\n     * @param entry the calendar entry\n     * @return appropriate entry, or null if not found\n     */\n    private TreeEntry<E> getEntry(Entry<?> entry) {\n        TreeEntry<E> t = root;\n        while (t != null) {\n            int cmp = compareLongs(getLow(entry), t.low);\n            if (cmp == 0)\n                cmp = compareLongs(getHigh(entry), t.high);\n            if (cmp == 0)\n                cmp = entry.hashCode() - t.value.hashCode();\n\n            if (cmp < 0) {\n                t = t.left;\n            } else if (cmp > 0) {\n                t = t.right;\n            } else {\n                return t;\n            }\n        }\n\n        return null;\n    }\n\n    private TreeEntry<E> addEntry(E entry) {\n        Objects.requireNonNull(entry, \"null entry is not supported\");\n\n        String id = entry.getId();\n        if (entryIDs.contains(id)) {\n            // TODO: reactivate this check, currently does not work when the start and end time\n            // of an entry get changed inside the EntryDetailView (two lambda expressions being evaluated\n            // in parallel).\n//            throw new IllegalArgumentException(\"an entry with ID = \" + entry.getId() + \" was already added to the calendar\");\n        }\n\n        entryIDs.add(id);\n\n        TreeEntry<E> t = root;\n        if (t == null) {\n            root = new TreeEntry<>(getLow(entry), getHigh(entry),\n                    entry, null);\n            treeSize = 1;\n            return root;\n        }\n\n        long cmp;\n        TreeEntry<E> parent;\n\n        do {\n            parent = t;\n            cmp = compareLongs(getLow(entry), t.low);\n            if (cmp == 0) {\n                cmp = compareLongs(getHigh(entry), t.high);\n                if (cmp == 0)\n                    cmp = entry.hashCode() - t.value.hashCode();\n            }\n\n            if (cmp < 0) {\n                t = t.left;\n            } else if (cmp > 0) {\n                t = t.right;\n            } else {\n                return null;\n            }\n        } while (t != null);\n\n        TreeEntry<E> e = new TreeEntry<>(getLow(entry), getHigh(entry),\n                entry, parent);\n        if (cmp < 0) {\n            parent.left = e;\n        } else {\n            parent.right = e;\n        }\n\n        fixAfterInsertion(e);\n        treeSize++;\n\n        return e;\n    }\n\n    private static int compareLongs(long val1, long val2) {\n        return val1 < val2 ? -1 : (val1 == val2 ? 0 : 1);\n    }\n\n    // This part of code was copied from java.util.TreeMap\n\n    // Red-black mechanics\n\n    private static final boolean RED = false;\n    private static final boolean BLACK = true;\n\n    /**\n     * Internal Entry class.\n     *\n     * @author koop\n     *\n     * @param <V>\n     */\n    private static final class TreeEntry<V> {\n        private long low;\n        private long high;\n        private V value;\n        private long maxHigh;\n        private TreeEntry<V> left;\n        private TreeEntry<V> right;\n        private TreeEntry<V> parent;\n        private boolean color = BLACK;\n\n        /**\n         * Make a new cell with given key, value, and parent, and with\n         * <tt>null</tt> child links, and BLACK color.\n         */\n        TreeEntry(long low, long high, V value, TreeEntry<V> parent) {\n            this.low = low;\n            this.high = high;\n            this.value = value;\n            this.parent = parent;\n            this.maxHigh = high;\n        }\n\n        @Override\n        public String toString() {\n            return \"[\" + Instant.ofEpochMilli(low) + \" - \"\n                    + Instant.ofEpochMilli(high) + \"]=\" + value;\n        }\n\n        public TreeEntry<V> getLeft() {\n            return left;\n        }\n\n        public TreeEntry<V> getRight() {\n            return right;\n        }\n    }\n\n    /**\n     * Returns the successor of the specified Entry, or null if no such.\n     *\n     * @param <V> the value type\n     */\n    private static <V> TreeEntry<V> successor(TreeEntry<V> t) {\n        if (t == null) {\n            return null;\n        } else if (t.right != null) {\n            TreeEntry<V> p = t.right;\n            while (p.left != null) {\n                p = p.left;\n            }\n            return p;\n        } else {\n            TreeEntry<V> p = t.parent;\n            TreeEntry<V> ch = t;\n            while (p != null && ch == p.right) {\n                ch = p;\n                p = p.parent;\n            }\n            return p;\n        }\n    }\n\n    /**\n     * Balancing operations.\n     *\n     * Implementations of rebalancings during insertion and deletion are\n     * slightly different than the CLR version. Rather than using dummy\n     * nilnodes, we use a set of accessors that deal properly with null. They\n     * are used to avoid messiness surrounding nullness checks in the main\n     * algorithms.\n     */\n\n    private static <V> boolean colorOf(TreeEntry<V> p) {\n        return p == null ? BLACK : p.color;\n    }\n\n    private static <V> TreeEntry<V> parentOf(TreeEntry<V> p) {\n        return p == null ? null : p.parent;\n    }\n\n    private static <V> void setColor(TreeEntry<V> p, boolean c) {\n        if (p != null) {\n            p.color = c;\n        }\n    }\n\n    private static <V> TreeEntry<V> leftOf(TreeEntry<V> p) {\n        return (p == null) ? null : p.left;\n    }\n\n    private static <V> TreeEntry<V> rightOf(TreeEntry<V> p) {\n        return (p == null) ? null : p.right;\n    }\n\n    /* From CLR */\n    private void rotateLeft(TreeEntry<E> p) {\n        if (p != null) {\n            TreeEntry<E> r = p.right;\n            p.right = r.left;\n            if (r.left != null) {\n                r.left.parent = p;\n            }\n            r.parent = p.parent;\n            if (p.parent == null) {\n                root = r;\n            } else if (p.parent.left == p) {\n                p.parent.left = r;\n            } else {\n                p.parent.right = r;\n            }\n            r.left = p;\n            p.parent = r;\n\n            // Original C code:\n            // x->maxHigh=ITMax(x->left->maxHigh,ITMax(x->right->maxHigh,x->high))\n            // Original C Code:\n            // y->maxHigh=ITMax(x->maxHigh,ITMax(y->right->maxHigh,y->high))\n            p.maxHigh = Math.max(\n                    p.left != null ? p.left.maxHigh : Long.MIN_VALUE,\n                    Math.max(p.right != null ? p.right.maxHigh : Long.MIN_VALUE,\n                            p.high));\n            r.maxHigh = Math.max(p.maxHigh,\n                    Math.max(r.right != null ? r.right.maxHigh : Long.MIN_VALUE,\n                            r.high));\n        }\n    }\n\n    /* From CLR */\n    private void rotateRight(TreeEntry<E> p) {\n        if (p != null) {\n            TreeEntry<E> l = p.left;\n            p.left = l.right;\n            if (l.right != null) {\n                l.right.parent = p;\n            }\n            l.parent = p.parent;\n            if (p.parent == null) {\n                root = l;\n            } else if (p.parent.right == p) {\n                p.parent.right = l;\n            } else {\n                p.parent.left = l;\n            }\n            l.right = p;\n            p.parent = l;\n\n            // Original C code:\n            // y->maxHigh=ITMax(y->left->maxHigh,ITMax(y->right->maxHigh,y->high))\n            // Original C code:\n            // x->maxHigh=ITMax(x->left->maxHigh,ITMax(y->maxHigh,x->high))\n            p.maxHigh = Math.max(\n                    p.left != null ? p.left.maxHigh : Long.MIN_VALUE,\n                    Math.max(p.right != null ? p.right.maxHigh : Long.MIN_VALUE,\n                            p.high));\n            l.maxHigh = Math.max(p.maxHigh, Math.max(\n                    l.left != null ? l.left.maxHigh : Long.MIN_VALUE, l.high));\n        }\n    }\n\n    /* From CLR */\n    private void fixAfterInsertion(TreeEntry<E> x) {\n\n        fixUpMaxHigh(x.parent); // augmented interval tree\n\n        x.color = RED;\n\n        while (x != null && x != root && x.parent.color == RED) {\n            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {\n                TreeEntry<E> y = rightOf(parentOf(parentOf(x)));\n                if (colorOf(y) == RED) {\n                    setColor(parentOf(x), BLACK);\n                    setColor(y, BLACK);\n                    setColor(parentOf(parentOf(x)), RED);\n                    x = parentOf(parentOf(x));\n                } else {\n                    if (x == rightOf(parentOf(x))) {\n                        x = parentOf(x);\n                        rotateLeft(x);\n                    }\n                    setColor(parentOf(x), BLACK);\n                    setColor(parentOf(parentOf(x)), RED);\n                    rotateRight(parentOf(parentOf(x)));\n                }\n            } else {\n                TreeEntry<E> y = leftOf(parentOf(parentOf(x)));\n                if (colorOf(y) == RED) {\n                    setColor(parentOf(x), BLACK);\n                    setColor(y, BLACK);\n                    setColor(parentOf(parentOf(x)), RED);\n                    x = parentOf(parentOf(x));\n                } else {\n                    if (x == leftOf(parentOf(x))) {\n                        x = parentOf(x);\n                        rotateRight(x);\n                    }\n                    setColor(parentOf(x), BLACK);\n                    setColor(parentOf(parentOf(x)), RED);\n                    rotateLeft(parentOf(parentOf(x)));\n                }\n            }\n        }\n        root.color = BLACK;\n    }\n\n    /**\n     * Delete node p, and then rebalance the tree.\n     */\n    private void deleteEntry(TreeEntry<E> p) {\n        entryIDs.remove(p.value.getId());\n\n        treeSize--;\n\n        // If strictly internal, copy successor's element to p and then make p\n        // point to successor.\n        if (p.left != null && p.right != null) {\n            TreeEntry<E> s = successor(p);\n            p.low = s.low;\n            p.high = s.high;\n            p.value = s.value;\n            p.maxHigh = s.maxHigh;\n            p = s;\n        }      // p has 2 children\n\n        // Start fixup at replacement node, if it exists.\n        TreeEntry<E> replacement = p.left != null ? p.left : p.right;\n\n        if (replacement != null) {\n            // Link replacement to parent\n            replacement.parent = p.parent;\n            if (p.parent == null) {\n                root = replacement;\n            } else if (p == p.parent.left) {\n                p.parent.left = replacement;\n            } else {\n                p.parent.right = replacement;\n            }\n\n            // Null out links so they are OK to use by fixAfterDeletion.\n            p.left = null;\n            p.right = null;\n            p.parent = null;\n\n            fixUpMaxHigh(replacement.parent); // augmented interval tree\n\n            // Fix replacement\n            if (p.color == BLACK) {\n                fixAfterDeletion(replacement);\n            }\n        } else if (p.parent == null) { // return if we are the only node.\n            root = null;\n        } else { // No children. Use self as phantom replacement and unlink.\n            if (p.color == BLACK) {\n                fixAfterDeletion(p);\n            }\n\n            if (p.parent != null) {\n                if (p == p.parent.left) {\n                    p.parent.left = null;\n                } else if (p == p.parent.right) {\n                    p.parent.right = null;\n                }\n\n                fixUpMaxHigh(p.parent); // augmented interval tree\n\n                p.parent = null;\n            }\n        }\n    }\n\n    /* From CLR */\n    private void fixAfterDeletion(TreeEntry<E> x) {\n        while (x != root && colorOf(x) == BLACK) {\n            if (x == leftOf(parentOf(x))) {\n                TreeEntry<E> sib = rightOf(parentOf(x));\n\n                if (colorOf(sib) == RED) {\n                    setColor(sib, BLACK);\n                    setColor(parentOf(x), RED);\n                    rotateLeft(parentOf(x));\n                    sib = rightOf(parentOf(x));\n                }\n\n                if (colorOf(leftOf(sib)) == BLACK\n                        && colorOf(rightOf(sib)) == BLACK) {\n                    setColor(sib, RED);\n                    x = parentOf(x);\n                } else {\n                    if (colorOf(rightOf(sib)) == BLACK) {\n                        setColor(leftOf(sib), BLACK);\n                        setColor(sib, RED);\n                        rotateRight(sib);\n                        sib = rightOf(parentOf(x));\n                    }\n                    setColor(sib, colorOf(parentOf(x)));\n                    setColor(parentOf(x), BLACK);\n                    setColor(rightOf(sib), BLACK);\n                    rotateLeft(parentOf(x));\n                    x = root;\n                }\n            } else { // symmetric\n                TreeEntry<E> sib = leftOf(parentOf(x));\n\n                if (colorOf(sib) == RED) {\n                    setColor(sib, BLACK);\n                    setColor(parentOf(x), RED);\n                    rotateRight(parentOf(x));\n                    sib = leftOf(parentOf(x));\n                }\n\n                if (colorOf(rightOf(sib)) == BLACK\n                        && colorOf(leftOf(sib)) == BLACK) {\n                    setColor(sib, RED);\n                    x = parentOf(x);\n                } else {\n                    if (colorOf(leftOf(sib)) == BLACK) {\n                        setColor(rightOf(sib), BLACK);\n                        setColor(sib, RED);\n                        rotateLeft(sib);\n                        sib = leftOf(parentOf(x));\n                    }\n                    setColor(sib, colorOf(parentOf(x)));\n                    setColor(parentOf(x), BLACK);\n                    setColor(leftOf(sib), BLACK);\n                    rotateRight(parentOf(x));\n                    x = root;\n                }\n            }\n        }\n\n        setColor(x, BLACK);\n    }\n\n    private class TimeInterval {\n\n        private final Instant startTime;\n\n        private final Instant endTime;\n\n        public TimeInterval(Instant startTime, Instant endTime) {\n            requireNonNull(startTime);\n            requireNonNull(endTime);\n\n            if (startTime.isAfter(endTime)) {\n                throw new IllegalArgumentException(\n                        \"start time can not be after end time, start = \"\n                                + startTime + \", end = \" + endTime);\n            }\n\n            this.startTime = startTime;\n            this.endTime = endTime;\n        }\n\n        public Instant getStartTime() {\n            return startTime;\n        }\n\n        public Instant getEndTime() {\n            return endTime;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/LoadEvent.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport javafx.event.Event;\nimport javafx.event.EventType;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.List;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * An event fired by the framework to inform the application that data will be\n * required for the time interval defined by the given start and end date. This\n * type of event can be used to implement a lazy loading strategy.\n *\n * <h2>Code Example</h2>\n * The following code snippet was taken from the Google calendar application included\n * in the distribution.\n * <pre>\n * CalendarView calendarView = ...\n * calendarView.addEventFilter(LoadEvent.LOAD, evt -&gt; {\n *    for (CalendarSource source : evt.getCalendarSources()) {\n *      if (source instanceof GoogleAccount) {\n *         GoogleAccount account = (GoogleAccount) source;\n *\n *         Thread loadThread = new Thread() {\n *            public void run() {\n *               for (Calendar calendar : account.getCalendars()) {\n *                  GoogleCalendar googleCalendar = (GoogleCalendar) calendar;\n *                  googleCalendar.load(evt.getStartDate(), evt.getEndDate(), evt.getZoneId());\n *               }\n *            }\n *         };\n *\n *         loadThread.setDaemon(true);\n *         loadThread.start();\n *\n *         break;\n *      }\n *   }\n * });\n * </pre>\n */\npublic final class LoadEvent extends Event {\n\n    private static final long serialVersionUID = -2691268182059394731L;\n\n    /**\n     * Gets fired frequently by the framework to indicate that data for the\n     * given date range is required to be present in the calendars.\n     */\n    public static final EventType<LoadEvent> LOAD = new EventType<>(Event.ANY,\n            \"LOAD\");\n\n    private final List<CalendarSource> calendarSources;\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final ZoneId zoneId;\n\n    private final String sourceName;\n\n    /**\n     * Constructs a new load event.\n     *\n     * @param eventType\n     *            the type of the load event\n     * @param sourceName\n     *            the name of the source where the event originated, e.g.\n     *            \"DayView\"\n     * @param calendarSources\n     *            the affected calendar sources\n     * @param startDate\n     *            the start date of the time interval\n     * @param endDate\n     *            the end date of the time interval\n     * @param zoneId\n     *            the time zone\n     */\n    public LoadEvent(EventType<LoadEvent> eventType, String sourceName,\n                     List<CalendarSource> calendarSources, LocalDate startDate,\n                     LocalDate endDate, ZoneId zoneId) {\n        super(eventType);\n\n        this.sourceName = requireNonNull(sourceName);\n        this.calendarSources = requireNonNull(calendarSources);\n        this.startDate = requireNonNull(startDate);\n        this.endDate = requireNonNull(endDate);\n        this.zoneId = requireNonNull(zoneId);\n    }\n\n    /**\n     * A human readable name of the control that triggered the load event, e.g.\n     * \"Day View\" or \"Month View\". Mainly used for debugging purposes.\n     *\n     * @return the name of the control requesting the data\n     */\n    public String getSourceName() {\n        return sourceName;\n    }\n\n    /**\n     * The calendar sources that are affected by the load event.\n     *\n     * @return the calendar sources\n     */\n    public List<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    /**\n     * The start of the loaded time interval.\n     *\n     * @return the start date\n     */\n    public LocalDate getStartDate() {\n        return startDate;\n    }\n\n    /**\n     * The end of the loaded time interval.\n     *\n     * @return the end date\n     */\n    public LocalDate getEndDate() {\n        return endDate;\n    }\n\n    /**\n     * The time zone used for the load.\n     *\n     * @return the time zone\n     */\n    public ZoneId getZoneId() {\n        return zoneId;\n    }\n\n    /**\n     * Convenience method to return a zoned date time based on the given start\n     * date and time zone. Uses {@link LocalTime#MIN} as time.\n     *\n     * @return the start time defined by this event\n     */\n    public ZonedDateTime getStartTime() {\n        return ZonedDateTime.of(startDate, LocalTime.MIN, zoneId);\n    }\n\n    /**\n     * Convenience method to return a zoned date time based on the given end\n     * date and time zone. Uses {@link LocalTime#MAX} as time.\n     *\n     * @return the start time defined by this event\n     */\n    public ZonedDateTime getEndTime() {\n        return ZonedDateTime.of(endDate, LocalTime.MAX, zoneId);\n    }\n\n    @Override\n    public String toString() {\n        return \"LoadEvent [sourceName=\" + sourceName + \", startDate=\"\n                + startDate + \", endDate=\" + endDate + \", zoneId=\" + zoneId\n                + \", calendarSources=\" + calendarSources + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/Marker.java",
    "content": "package com.calendarfx.model;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.time.ZonedDateTime;\n\npublic class Marker {\n\n    public Marker() {\n    }\n\n    private final BooleanProperty movable = new SimpleBooleanProperty(this, \"movable\", true);\n\n    public final boolean isMovable() {\n        return movable.get();\n    }\n\n    public final BooleanProperty movableProperty() {\n        return movable;\n    }\n\n    public final void setMovable(boolean movable) {\n        this.movable.set(movable);\n    }\n\n    private final ObjectProperty<ZonedDateTime> time = new SimpleObjectProperty<>(this, \"time\", ZonedDateTime.now());\n\n    public ZonedDateTime getTime() {\n        return time.get();\n    }\n\n    public ObjectProperty<ZonedDateTime> timeProperty() {\n        return time;\n    }\n\n    public void setTime(ZonedDateTime time) {\n        this.time.set(time);\n    }\n\n    private final StringProperty title = new SimpleStringProperty(this, \"title\", \"Untitled\");\n\n    public String getTitle() {\n        return title.get();\n    }\n\n    public StringProperty titleProperty() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title.set(title);\n    }\n\n    private final StringProperty style = new SimpleStringProperty(this, \"style\");\n\n    public final String getStyle() {\n        return style.get();\n    }\n\n    public final StringProperty styleProperty() {\n        return style;\n    }\n\n    public final void setStyle(String style) {\n        this.style.set(style);\n    }\n\n    private final ObservableList<String> styleClass = FXCollections.observableArrayList();\n\n    public final ObservableList<String> getStyleClass() {\n        return styleClass;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/Resource.java",
    "content": "package com.calendarfx.model;\n\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.ResourcesView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyListProperty;\nimport javafx.beans.property.ReadOnlyListWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A resource represents a person or a machine. Resources can be edited via\n * the {@link ResourcesView}. A typical use case would be the allocation of\n * personnel in a hairdresser salon. A resource might be available or not.\n * This can be expressed via the {@link #availabilityCalendarProperty()}.\n *\n * @param <T> the type of the wrapped / referenced business object (person or machine).\n *\n * @see ResourcesView#getResources()\n * @see DateControl#editAvailabilityProperty()\n * @see DateControl#availabilityGridProperty()\n * @see DateControl#availabilityFillProperty()\n */\npublic class Resource<T> {\n\n    private final InvalidationListener updateCalendarListListener = (Observable it) -> updateCalendarList();\n\n    private final WeakInvalidationListener weakUpdateCalendarListListener = new WeakInvalidationListener(updateCalendarListListener);\n\n    public Resource() {\n        getCalendarSources().addListener(weakUpdateCalendarListListener);\n\n        /*\n         * Every resource is initially populated with a default source and calendar.\n         * We borrow the i18n strings from DateControl.\n         */\n        Calendar defaultCalendar = new Calendar(Messages.getString(\"DateControl.DEFAULT_CALENDAR_NAME\"));\n        defaultCalendar.setUserObject(this);\n\n        CalendarSource defaultCalendarSource = new CalendarSource(Messages.getString(\"DateControl.DEFAULT_CALENDAR_SOURCE_NAME\"));\n        defaultCalendarSource.getCalendars().add(defaultCalendar);\n        getCalendarSources().add(defaultCalendarSource);\n    }\n\n    public Resource(T userObject) {\n        this();\n        setUserObject(userObject);\n    }\n\n    private final ObjectProperty<T> userObject = new SimpleObjectProperty<>(this, \"userObject\");\n\n    public final T getUserObject() {\n        return userObject.get();\n    }\n\n    /**\n     * An (optional) user object.\n     *\n     * @return the user object (e.g. the person or the calendar data source).\n     */\n    public final ObjectProperty<T> userObjectProperty() {\n        return userObject;\n    }\n\n    public final void setUserObject(T userObject) {\n        this.userObject.set(userObject);\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections.observableArrayList();\n\n    /**\n     * The list of all calendar sources attached to this resource.\n     *\n     * @return the calendar sources\n     * @see DateControl#getCalendarSources()\n     */\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ReadOnlyListWrapper<Calendar> calendars = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());\n\n    /**\n     * A list that contains all calendars found in all calendar sources\n     * currently attached to this resource. This is a convenience list that\n     * \"flattens\" the two level structure of sources and their calendars. It is\n     * a read-only list because calendars can not be added directly to a resource.\n     * Instead, they are added to calendar sources and those sources are\n     * then added to the control.\n     *\n     * @return the list of all calendars available for this resource\n     * @see #getCalendarSources()\n     */\n    public final ReadOnlyListProperty<Calendar> calendarsProperty() {\n        return calendars.getReadOnlyProperty();\n    }\n\n    private final ObservableList<Calendar> unmodifiableCalendars = FXCollections.unmodifiableObservableList(calendars.get());\n\n    /**\n     * Returns the value of {@link #calendarsProperty()}.\n     *\n     * @return the list of all calendars available for this control\n     */\n    public final ObservableList<Calendar> getCalendars() {\n        return unmodifiableCalendars;\n    }\n\n    public final ObjectProperty<Calendar> availabilityCalendar = new SimpleObjectProperty<>(this, \"availabilityCalendar\", new Calendar());\n\n    public Calendar getAvailabilityCalendar() {\n        return availabilityCalendar.get();\n    }\n\n    /**\n     * A resource might be available or not. This can be determined via the \"availability\"\n     * calendar.\n     *\n     * @return the resource's availability calendar\n     *\n     * @see DateControl#editAvailabilityProperty()\n     * @see DateControl#availabilityGridProperty()\n     * @see DateControl#availabilityFillProperty()\n     */\n    public ObjectProperty<Calendar> availabilityCalendarProperty() {\n        return availabilityCalendar;\n    }\n\n    public void setAvailabilityCalendar(Calendar availabilityCalendar) {\n        this.availabilityCalendar.set(availabilityCalendar);\n    }\n\n    private void updateCalendarList() {\n        List<Calendar> removedCalendars = new ArrayList<>(calendars);\n        List<Calendar> newCalendars = new ArrayList<>();\n        for (CalendarSource source : getCalendarSources()) {\n            for (Calendar calendar : source.getCalendars()) {\n                if (calendars.contains(calendar)) {\n                    removedCalendars.remove(calendar);\n                } else {\n                    newCalendars.add(calendar);\n                }\n            }\n            source.getCalendars().removeListener(weakUpdateCalendarListListener);\n            source.getCalendars().addListener(weakUpdateCalendarListListener);\n        }\n\n        calendars.addAll(newCalendars);\n        calendars.removeAll(removedCalendars);\n    }\n\n    @Override\n    public String toString() {\n        if (getUserObject() != null) {\n            return getUserObject().toString();\n        }\n\n        return super.toString();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/model/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * Classes for modeling calendars and entries.\n */\npackage com.calendarfx.model;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/CalendarFX.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\n/**\n * Common superclass for all controls in this framework.\n */\npublic final class CalendarFX {\n\n    private static String version;\n\n    /**\n     * Returns the CalendarFX version number in the format major.minor.bug\n     * (1.0.0).\n     *\n     * @return the CalendarFX version number\n     */\n    public static String getVersion() {\n        if (version == null) {\n\n            InputStream stream = CalendarFX.class.getResourceAsStream(\"version.properties\");\n            Properties props = new Properties();\n            try {\n                props.load(stream);\n            } catch (IOException ex) {\n                LoggingDomain.CONFIG.throwing(CalendarFX.class.getName(), \"getVersion()\", ex);\n            }\n\n            version = props.getProperty(\"calendarfx.version\", \"1.0.0\");\n\n            LoggingDomain.CONFIG.info(\"CalendarFX Version: \" + version);\n        }\n        return version;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/LoggingDomain.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.util;\n\nimport java.util.logging.Logger;\n\n/**\n * Globally defined loggers. Alternative approach to using class based loggers.\n */\npublic final class LoggingDomain {\n\n    private static final String PREFIX = \"com.calendarfx\"; \n\n    /**\n     * Logger used for anything related to the configuration of the calendar.\n     */\n    public static final Logger CONFIG = Logger.getLogger(PREFIX + \".config\"); \n\n    /**\n     * Logger used for anything related to the creation, fireing, and handling\n     * of events.\n     */\n    public static final Logger EVENTS = Logger.getLogger(PREFIX + \".events\"); \n\n    /**\n     * Logger used for anything related to the model, adding / removing entries.\n     */\n    public static final Logger MODEL = Logger.getLogger(PREFIX + \".model\"); \n\n    /**\n     * Logger used for anything related to the creation of the view.\n     */\n    public static final Logger VIEW = Logger.getLogger(PREFIX + \".view\"); \n\n    /**\n     * Logger used for the search service.\n     */\n    public static final Logger SEARCH = Logger.getLogger(PREFIX + \".search\"); \n\n    /**\n     * Logger used for anything related to the editing of entries.\n     */\n    public static final Logger EDITING = Logger.getLogger(PREFIX + \".editing\"); \n\n    /**\n     * Logger used for anything related to recurrence.\n     */\n    public static final Logger RECURRENCE = Logger.getLogger(PREFIX + \".recurrence\"); \n\n    /**\n     * Logger used for anything related to printing.\n     */\n    public static final Logger PRINTING = Logger.getLogger(PREFIX + \".printing\"); \n\n    /**\n     * Logger used for anything related to performance.\n     */\n    public static final Logger PERFORMANCE = Logger.getLogger(PREFIX + \".performance\"); \n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/LoggingFormatter.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.util;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.logging.Formatter;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\n/**\n * A formatter for the logging framework. Formats logging messages in a very\n * compact format.\n *\n * @author Dirk Lemmermann\n */\npublic class LoggingFormatter extends Formatter {\n\n    private static final DateTimeFormatter formatter = DateTimeFormatter\n            .ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT);\n\n    private static int MAX_LEVEL_SIZE = 0;\n\n    static {\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.INFO.getLocalizedName()\n                .length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.CONFIG\n                .getLocalizedName().length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.FINE.getLocalizedName()\n                .length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.FINER\n                .getLocalizedName().length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.FINEST\n                .getLocalizedName().length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.SEVERE\n                .getLocalizedName().length());\n        MAX_LEVEL_SIZE = Math.max(MAX_LEVEL_SIZE, Level.WARNING\n                .getLocalizedName().length());\n    }\n\n    /**\n     * Format the given LogRecord.\n     *\n     * @param record\n     *            the log record to be formatted.\n     * @return a formatted log record\n     */\n    @Override\n    public synchronized String format(LogRecord record) {\n        StringBuilder sb = new StringBuilder();\n\n        ZonedDateTime timestamp = ZonedDateTime.ofInstant(\n                Instant.ofEpochMilli(record.getMillis()),\n                ZoneId.systemDefault());\n\n        sb.append(formatter.format(timestamp));\n        sb.append(\" | \");\n\n        if (record.getSourceClassName() != null) {\n            sb.append(truncate(\n                    record.getSourceClassName().substring(\n                            record.getSourceClassName().lastIndexOf('.') + 1),\n                    30));\n        } else {\n            sb.append(truncate(record.getLoggerName(), 10));\n        }\n\n        sb.append(\" | \");\n\n        if (record.getSourceMethodName() != null) {\n            sb.append(truncate(record.getSourceMethodName(), 30));\n        }\n\n        sb.append(\" | \");\n\n        String message = formatMessage(record);\n        sb.append(truncate(record.getLevel().getLocalizedName(), MAX_LEVEL_SIZE));\n\n        sb.append(\" | \");\n\n        sb.append(message);\n\n        sb.append(System.getProperty(\"line.separator\"));\n        if (record.getThrown() != null) {\n            StringWriter sw = new StringWriter();\n            PrintWriter pw = new PrintWriter(sw);\n            record.getThrown().printStackTrace(pw);\n            pw.close();\n            sb.append(sw);\n        }\n        return sb.toString();\n    }\n\n    private CharSequence truncate(String s, int n) {\n        if (s.length() > n) {\n            s = s.substring(0, n);\n        }\n        return String.format(\"%1$-\" + n + \"s\", s);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/ViewHelper.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.util;\n\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport impl.com.calendarfx.view.DayViewScrollPane;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Bounds;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Rectangle2D;\nimport javafx.scene.Node;\nimport javafx.stage.Screen;\nimport org.controlsfx.control.PopOver.ArrowLocation;\n\nimport java.time.Instant;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\n\n@SuppressWarnings(\"javadoc\")\npublic final class ViewHelper {\n\n    public static double getTimeLocation(DayViewBase view, LocalTime time) {\n        return getTimeLocation(view, ZonedDateTime.of(view.getDate(), time, view.getZoneId()));\n    }\n\n    public static double getTimeLocation(DayViewBase view, LocalTime time, boolean prefHeight) {\n        return getTimeLocation(view, ZonedDateTime.of(view.getDate(), time, view.getZoneId()), prefHeight);\n    }\n\n    public static double getTimeLocation(DayViewBase view, ZonedDateTime time) {\n        return getTimeLocation(view, time, false);\n    }\n\n    public static double getTimeLocation(DayViewBase view, Instant instant) {\n        return getTimeLocation(view, ZonedDateTime.ofInstant(instant, view.getZoneId()), false);\n    }\n\n    public static double getTimeLocation(DayViewBase view, ZonedDateTime zonedTime, boolean prefHeight) {\n        if (view.isScrollingEnabled()) {\n            final Instant scrollInstant = view.getScrollTime().toInstant();\n            final double mpp = DayView.MILLIS_PER_HOUR / view.getHourHeight();\n            final long millis = zonedTime.toInstant().toEpochMilli() - scrollInstant.toEpochMilli();\n            return millis / mpp;\n        }\n\n        double availableHeight = view.getHeight();\n        if (prefHeight) {\n            availableHeight = view.prefHeight(-1);\n        }\n\n        long epochMilli = zonedTime.toInstant().toEpochMilli();\n\n        switch (view.getEarlyLateHoursStrategy()) {\n            case SHOW:\n                ZonedDateTime startTime = view.getZonedDateTimeMin();\n                ZonedDateTime endTime = view.getZonedDateTimeMax();\n\n                long startMillis = startTime.toInstant().toEpochMilli();\n                long endMillis = endTime.toInstant().toEpochMilli();\n\n                double mpp = (endMillis - startMillis) / availableHeight;\n\n                return ((int) ((epochMilli - startMillis) / mpp)) + .5;\n            case HIDE:\n                startTime = view.getZonedDateTimeStart();\n                endTime = view.getZonedDateTimeEnd();\n\n                if (zonedTime.isBefore(startTime)) {\n                    return -1;\n                }\n\n                if (zonedTime.isAfter(endTime)) {\n                    return availableHeight;\n                }\n\n                startMillis = startTime.toInstant().toEpochMilli();\n                endMillis = endTime.toInstant().toEpochMilli();\n\n                mpp = (endMillis - startMillis) / availableHeight;\n\n                return ((int) ((epochMilli - startMillis) / mpp)) + .5;\n            case SHOW_COMPRESSED:\n                ZonedDateTime minTime = view.getZonedDateTimeMin();\n                ZonedDateTime maxTime = view.getZonedDateTimeMax();\n\n                startTime = view.getZonedDateTimeStart();\n                endTime = view.getZonedDateTimeEnd();\n\n                long earlyHours = ChronoUnit.HOURS.between(minTime, startTime);\n                long lateHours = ChronoUnit.HOURS.between(endTime, maxTime) + 1;\n\n                double hourHeightCompressed = view.getHourHeightCompressed();\n                double earlyHeight = hourHeightCompressed * earlyHours;\n                double lateHeight = hourHeightCompressed * lateHours;\n\n                if (zonedTime.isBefore(startTime)) {\n                    /*\n                     * Early compressed hours.\n                     */\n                    startMillis = minTime.toInstant().toEpochMilli();\n                    endMillis = startTime.toInstant().toEpochMilli();\n\n                    mpp = (endMillis - startMillis) / earlyHeight;\n\n                    return ((int) ((epochMilli - startMillis) / mpp)) + .5;\n                } else if (zonedTime.isAfter(endTime)) {\n                    /*\n                     * Late compressed hours.\n                     */\n                    startMillis = endTime.toInstant().toEpochMilli();\n                    endMillis = maxTime.toInstant().toEpochMilli();\n\n                    mpp = (endMillis - startMillis) / lateHeight;\n\n                    return ((int) ((epochMilli - startMillis) / mpp)) + (availableHeight - lateHeight) + .5;\n                } else {\n                    /*\n                     * Regular hours.\n                     */\n                    startMillis = startTime.toInstant().toEpochMilli();\n                    endMillis = endTime.toInstant().toEpochMilli();\n                    mpp = (endMillis - startMillis) / (availableHeight - earlyHeight - lateHeight);\n\n                    return earlyHeight + ((int) ((epochMilli - startMillis) / mpp)) + .5;\n                }\n            default:\n                return 0;\n        }\n    }\n\n    public static Instant getInstantAt(DayViewBase view, double y) {\n\n        ZonedDateTime zonedDateTime = view.getZonedDateTimeStart();\n\n        double availableHeight = view.getHeight();\n\n        switch (view.getEarlyLateHoursStrategy()) {\n            case SHOW:\n                long startMillis = view.getZonedDateTimeMin().toInstant().toEpochMilli();\n                long endMillis = view.getZonedDateTimeMax().toInstant().toEpochMilli();\n\n                double mpp = (endMillis - startMillis) / availableHeight;\n\n                long millis = (long) (mpp * y) + startMillis;\n\n                return Instant.ofEpochMilli(millis);\n            case HIDE:\n                ZonedDateTime startTime = view.getZonedDateTimeStart();\n                ZonedDateTime endTime = view.getZonedDateTimeEnd();\n\n                startMillis = startTime.toInstant().toEpochMilli();\n                endMillis = endTime.toInstant().toEpochMilli();\n\n                mpp = (endMillis - startMillis) / availableHeight;\n\n                millis = (long) (mpp * y) + startMillis;\n\n                return Instant.ofEpochMilli(millis);\n            case SHOW_COMPRESSED:\n                startTime = view.getZonedDateTimeStart();\n                endTime = view.getZonedDateTimeEnd();\n\n                ZonedDateTime minTime = view.getZonedDateTimeMin();\n                ZonedDateTime maxTime = view.getZonedDateTimeMax();\n\n                long earlyHours = ChronoUnit.HOURS.between(minTime, startTime);\n                long lateHours = ChronoUnit.HOURS.between(endTime, maxTime) + 1;\n\n                double hourHeightCompressed = view.getHourHeightCompressed();\n                double earlyHeight = hourHeightCompressed * earlyHours;\n                double lateHeight = hourHeightCompressed * lateHours;\n\n                if (y < earlyHeight) {\n                    /*\n                     * Early compressed hours.\n                     */\n                    startMillis = minTime.toInstant().toEpochMilli();\n                    endMillis = startTime.toInstant().toEpochMilli();\n\n                    mpp = (endMillis - startMillis) / earlyHeight;\n\n                    millis = (long) (mpp * y) + startMillis;\n\n                    return Instant.ofEpochMilli(millis);\n                } else if (y > availableHeight - lateHeight) {\n                    /*\n                     * Late compressed hours.\n                     */\n                    startMillis = endTime.toInstant().toEpochMilli();\n                    endMillis = maxTime.toInstant().toEpochMilli();\n\n                    mpp = (endMillis - startMillis) / lateHeight;\n\n                    millis = (long) (mpp * (y - (availableHeight - lateHeight))) + startMillis;\n\n                    return Instant.ofEpochMilli(millis);\n                } else {\n                    /*\n                     * Regular hours.\n                     */\n                    startMillis = startTime.toInstant().toEpochMilli();\n                    endMillis = endTime.toInstant().toEpochMilli();\n\n                    mpp = (endMillis - startMillis) / (availableHeight - earlyHeight - lateHeight);\n\n                    millis = (long) (mpp * (y - earlyHeight)) + startMillis;\n\n                    return Instant.ofEpochMilli(millis);\n                }\n            default:\n                return zonedDateTime.toInstant();\n        }\n    }\n\n    public static ArrowLocation findPopOverArrowLocation(Node view) {\n        Bounds localBounds = view.getBoundsInLocal();\n        Bounds entryBounds = view.localToScreen(localBounds);\n\n        ObservableList<Screen> screens = Screen.getScreensForRectangle(\n                entryBounds.getMinX(), entryBounds.getMinY(),\n                entryBounds.getWidth(), entryBounds.getHeight());\n\n        if (screens.isEmpty()) {\n            return null;\n\n        }\n        Rectangle2D screenBounds = screens.get(0).getVisualBounds();\n\n        double spaceLeft = entryBounds.getMinX();\n        double spaceRight = screenBounds.getWidth() - entryBounds.getMaxX();\n        double spaceTop = entryBounds.getMinY();\n        double spaceBottom = screenBounds.getHeight() - entryBounds.getMaxY();\n\n        if (spaceLeft > spaceRight) {\n            if (spaceTop > spaceBottom) {\n                return ArrowLocation.RIGHT_BOTTOM;\n            }\n            return ArrowLocation.RIGHT_TOP;\n        }\n\n        if (spaceTop > spaceBottom) {\n            return ArrowLocation.LEFT_BOTTOM;\n        }\n\n        return ArrowLocation.LEFT_TOP;\n    }\n\n    public static Point2D findPopOverArrowPosition(Node node, double screenY, double arrowSize, ArrowLocation arrowLocation) {\n\n        Bounds entryBounds = node.localToScreen(node.getBoundsInLocal());\n\n        double screenX;\n\n        if (arrowLocation == ArrowLocation.LEFT_TOP || arrowLocation == ArrowLocation.LEFT_BOTTOM) {\n            screenX = entryBounds.getMaxX();\n        } else {\n            screenX = entryBounds.getMinX() - arrowSize;\n        }\n\n        return new Point2D(screenX, screenY);\n    }\n\n    public static void scrollToRequestedTime(DateControl control, DayViewScrollPane scrollPane) {\n        LocalTime requestedTime = control.getRequestedTime();\n        if (requestedTime != null) {\n            scrollPane.scrollToTime(requestedTime);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/WeakList.java",
    "content": "package com.calendarfx.util;\n\nimport java.lang.ref.WeakReference;\nimport java.util.AbstractList;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * A simple list wich holds only weak references to the original objects.\n */\npublic class WeakList<T> extends AbstractList<T> {\n\n    private final ArrayList<WeakReference<T>> items;\n\n    /**\n     * Creates new WeakList\n     */\n    public WeakList() {\n        items = new ArrayList<>();\n    }\n\n    public WeakList(Collection c) {\n        items = new ArrayList();\n        addAll(0, c);\n    }\n\n    public void add(int index, Object element) {\n        items.add(index, new WeakReference(element));\n    }\n\n    public Iterator<T> iterator() {\n        return new WeakListIterator();\n    }\n\n    public int size() {\n        removeReleased();\n        return items.size();\n    }\n\n    public T get(int index) {\n        return items.get(index).get();\n    }\n\n    private void removeReleased() {\n        List<WeakReference<T>> temp = new ArrayList<>(items);\n        temp.forEach(ref -> {\n            if (ref.get() == null) {\n                items.remove(ref);\n            }\n        });\n    }\n\n    private class WeakListIterator implements Iterator<T> {\n\n        private final int n;\n        private int i;\n\n        public WeakListIterator() {\n            n = size();\n            i = 0;\n        }\n\n        public boolean hasNext() {\n            return i < n;\n        }\n\n        public T next() {\n            return get(i++);\n        }\n\n        public void remove() {\n            throw new UnsupportedOperationException();\n        }\n\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/util/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * Core classes used for logging, license management.\n */\npackage com.calendarfx.util;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/AgendaView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.AgendaViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.HPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.shape.Circle;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.time.format.TextStyle;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * The agenda view displays calendar entries in a list. The view can be\n * configured to look back a given number of days and also to look forward a\n * given number of days.\n *\n * <img src=\"doc-files/agenda-view.png\" alt=\"Agenda View\">\n */\npublic class AgendaView extends DateControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"agenda-view\";\n    private static final String AGENDA_CATEGORY = \"Agenda View\";\n\n    private final ListView<AgendaEntry> listView = new ListView<>();\n\n    /**\n     * Constructs a new agenda view.\n     */\n    public AgendaView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n        listView.setCellFactory(cbListView -> getCellFactory().call(this));\n        setContextMenu(buildContextMenu());\n    }\n\n    /**\n     * Returns the list view that will be used to display one cell for each day that\n     * contains at least one calendar entry.\n     *\n     * @return the list view used by this control\n     */\n    public final ListView<AgendaEntry> getListView() {\n        return listView;\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new AgendaViewSkin(this);\n    }\n\n    private ContextMenu buildContextMenu() {\n        ContextMenu menu = new ContextMenu();\n        Menu lookBackMenu = new Menu(Messages.getString(\"AgendaView.MENU_ITEM_LOOK_BACK\"));\n        Menu lookAheadMenu = new Menu(Messages.getString(\"AgendaView.MENU_ITEM_LOOK_AHEAD\"));\n\n        String format = Messages.getString(\"AgendaView.MENU_ITEM_DAYS\");\n\n        MenuItem lookBack0 = new MenuItem(MessageFormat.format(format, 0));\n        MenuItem lookBack10 = new MenuItem(MessageFormat.format(format, 10));\n        MenuItem lookBack20 = new MenuItem(MessageFormat.format(format, 20));\n        MenuItem lookBack30 = new MenuItem(MessageFormat.format(format, 30));\n        MenuItem lookAhead0 = new MenuItem(MessageFormat.format(format, 0));\n        MenuItem lookAhead10 = new MenuItem(MessageFormat.format(format, 10));\n        MenuItem lookAhead20 = new MenuItem(MessageFormat.format(format, 20));\n        MenuItem lookAhead30 = new MenuItem(MessageFormat.format(format, 30));\n        lookBackMenu.getItems().addAll(lookBack0, lookBack10, lookBack20, lookBack30);\n        lookAheadMenu.getItems().addAll(lookAhead0, lookAhead10, lookAhead20, lookAhead30);\n        menu.getItems().addAll(lookBackMenu, lookAheadMenu);\n\n        lookBack0.setOnAction(evt -> setLookBackPeriodInDays(0));\n        lookBack10.setOnAction(evt -> setLookBackPeriodInDays(10));\n        lookBack20.setOnAction(evt -> setLookBackPeriodInDays(20));\n        lookBack30.setOnAction(evt -> setLookBackPeriodInDays(30));\n\n        lookAhead0.setOnAction(evt -> setLookAheadPeriodInDays(0));\n        lookAhead10.setOnAction(evt -> setLookAheadPeriodInDays(10));\n        lookAhead20.setOnAction(evt -> setLookAheadPeriodInDays(20));\n        lookAhead30.setOnAction(evt -> setLookAheadPeriodInDays(30));\n\n        return menu;\n    }\n\n    private final IntegerProperty lookBackPeriodInDays = new SimpleIntegerProperty(this, \"lookBackPeriodInDays\", 0);\n\n    /**\n     * Stores the number of days to \"look back\" into the past when loading data.\n     *\n     * @return the number of days to look back\n     */\n    public final IntegerProperty lookBackPeriodInDaysProperty() {\n        return lookBackPeriodInDays;\n    }\n\n    /**\n     * Gets the value of {@link #lookBackPeriodInDaysProperty()}.\n     *\n     * @return the number of days to look back\n     */\n    public final int getLookBackPeriodInDays() {\n        return lookBackPeriodInDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #lookBackPeriodInDaysProperty()}.\n     *\n     * @param days the new number of days to look back\n     */\n    public final void setLookBackPeriodInDays(int days) {\n        if (days < 0) {\n            throw new IllegalArgumentException(\"days must be larger than or equal to 0\");\n        }\n        lookBackPeriodInDaysProperty().set(days);\n    }\n\n    private final IntegerProperty lookAheadPeriodInDays = new SimpleIntegerProperty(this, \"lookAheadPeriodInDays\", 30);\n\n    /**\n     * Stores the number of days to \"look ahead\" into the future when loading\n     * its data.\n     *\n     * @return the number of days to \"look ahead\"\n     */\n    public final IntegerProperty lookAheadPeriodInDaysProperty() {\n        return lookAheadPeriodInDays;\n    }\n\n    /**\n     * Returns the value of {@link #lookAheadPeriodInDaysProperty()}.\n     *\n     * @return the number of days to look ahead\n     */\n    public final int getLookAheadPeriodInDays() {\n        return lookAheadPeriodInDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #lookAheadPeriodInDaysProperty()}.\n     *\n     * @param days the number of days to look ahead\n     */\n    public final void setLookAheadPeriodInDays(int days) {\n        if (days < 0) {\n            throw new IllegalArgumentException(\"days must be larger than or equal to 0\");\n        }\n        lookAheadPeriodInDaysProperty().set(days);\n    }\n\n    private final BooleanProperty showStatusLabel = new SimpleBooleanProperty(this, \"showStatusLabel\", true);\n\n    public final BooleanProperty showStatusLabelProperty() {\n        return showStatusLabel;\n    }\n\n    public final boolean isShowStatusLabel() {\n        return showStatusLabelProperty().get();\n    }\n\n    public final void setShowStatusLabel(boolean showStatusLabel) {\n        showStatusLabelProperty().set(showStatusLabel);\n    }\n\n    private final ObjectProperty<Callback<AgendaView, ? extends AgendaEntryCell>> cellFactory = new SimpleObjectProperty<Callback<AgendaView, ? extends AgendaEntryCell>>(this, \"cellFactory\", view -> new AgendaEntryCell(this)) {\n        @Override\n        public void set(Callback<AgendaView, ? extends AgendaEntryCell> newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ObjectProperty<Callback<AgendaView, ? extends AgendaEntryCell>> cellFactoryProperty() {\n        return cellFactory;\n    }\n\n    public final Callback<AgendaView, ? extends AgendaEntryCell> getCellFactory() {\n        return cellFactoryProperty().get();\n    }\n\n    public final void setCellFactory(Callback<AgendaView, ? extends AgendaEntryCell> cellFactory) {\n        cellFactoryProperty().set(cellFactory);\n    }\n\n    private final ObjectProperty<DateTimeFormatter> formatter = new SimpleObjectProperty<>(this, \"formatter\", DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));\n\n    /**\n     * Gets the DateTimeFormatter property, which is use to provide the format on the TimeScale Labels. By default it\n     * has a value of {@link FormatStyle#LONG}\n     *\n     * @return the date formatter.\n     */\n    public final ObjectProperty<DateTimeFormatter> dateTimeFormatterProperty() {\n        return formatter;\n    }\n\n    /**\n     * Returns the value of {@link #dateTimeFormatterProperty()}\n     *\n     * @return a date time formatter\n     */\n    public final DateTimeFormatter getDateTimeFormatter() {\n        return dateTimeFormatterProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #dateTimeFormatterProperty()}\n     *\n     * @param formatter a date time formatter, not {@code null}\n     */\n    public final void setDateTimeFormatter(DateTimeFormatter formatter) {\n        requireNonNull(formatter);\n        dateTimeFormatterProperty().set(formatter);\n    }\n\n    /**\n     * Agenda entries are model objects that reference a collection of calendar\n     * entries for a specific date.\n     */\n    public static class AgendaEntry implements Comparable<AgendaEntry> {\n\n        private final LocalDate date;\n\n        public AgendaEntry(LocalDate date) {\n            this.date = requireNonNull(date);\n        }\n\n        public LocalDate getDate() {\n            return date;\n        }\n\n        private final List<Entry<?>> entries = new ArrayList<>();\n\n        public final List<Entry<?>> getEntries() {\n            return entries;\n        }\n\n        @Override\n        public int compareTo(AgendaEntry o) {\n            return getDate().compareTo(o.getDate());\n        }\n    }\n\n    /**\n     * A specialized list cell that is capable of displaying all entries currently assigned\n     * to a given day. Each cell features a header that shows the date information and a body\n     * that lists all entries. Each entry is visualized with an icon, a title text, and a\n     * time text.\n     *\n     * @see AgendaView#getListView()\n     * @see ListView#setCellFactory(javafx.util.Callback)\n     */\n    public static class AgendaEntryCell extends ListCell<AgendaEntry> {\n\n        private static final String AGENDA_VIEW_LIST_CELL = \"agenda-view-list-cell\";\n        private static final String AGENDA_VIEW_TIME_LABEL = \"time-label\";\n        private static final String AGENDA_VIEW_TITLE_LABEL = \"title-label\";\n        private static final String AGENDA_VIEW_BODY = \"body\";\n        private static final String AGENDA_VIEW_DATE_LABEL = \"date-label\";\n        private static final String AGENDA_VIEW_DATE_LABEL_TODAY = \"today\";\n        private static final String AGENDA_VIEW_WEEKDAY_LABEL = \"weekday-label\";\n        private static final String AGENDA_VIEW_WEEKDAY_LABEL_TODAY = \"today\";\n        private static final String AGENDA_VIEW_HEADER = \"header\";\n        private static final String AGENDA_VIEW_HEADER_TODAY = \"today\";\n        private static final String AGENDA_VIEW_BODY_SEPARATOR = \"separator\";\n\n        private DateTimeFormatter weekdayFormatter = DateTimeFormatter.ofPattern(Messages.getString(\"AgendaEntryCell.WEEKDAY_FORMAT\"));\n        private DateTimeFormatter mediumDateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);\n        private DateTimeFormatter shortDateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);\n        private DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n\n        private Label weekdayLabel;\n        private Label dateLabel;\n        private GridPane gridPane;\n        private BorderPane headerPane;\n        private final boolean headerPaneVisible;\n\n        private final AgendaView agendaView;\n\n        /**\n         * Constructs a new cell that will work with the given agenda view.\n         *\n         * @param view the parent list view\n         */\n        public AgendaEntryCell(AgendaView view) {\n            this(view, true);\n        }\n\n        /**\n         * Constructs a new cell that will work with the given agenda view.\n         *\n         * @param view              the parent list view\n         * @param headerPaneVisible flag to control the visibility of the cell's header.\n         */\n        public AgendaEntryCell(AgendaView view, boolean headerPaneVisible) {\n            this.agendaView = Objects.requireNonNull(view);\n            this.headerPaneVisible = headerPaneVisible;\n\n            BorderPane borderPane = new BorderPane();\n            borderPane.getStyleClass().add(\"container\");\n            borderPane.setTop(createHeader());\n            borderPane.setCenter(createBody());\n\n            setGraphic(borderPane);\n            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n            getStyleClass().add(AGENDA_VIEW_LIST_CELL);\n        }\n\n        /**\n         * Creates the node used for the body part of each cell.\n         * <p>\n         * In this default implementation the body consists of a grid pane with\n         * three columns. The middle column is used for showing the title of\n         * calendar entries. This column will get whatever space is left after\n         * the icon and the time column have used what they need. This means\n         * that a very long title will automatically be truncated.\n         *\n         * @return the body node\n         */\n        protected Node createBody() {\n            // icon column\n            ColumnConstraints iconColumn = new ColumnConstraints();\n\n            // title column\n            ColumnConstraints descriptionColumn = new ColumnConstraints();\n            descriptionColumn.setFillWidth(true);\n            descriptionColumn.setHgrow(Priority.SOMETIMES);\n            descriptionColumn.setMinWidth(0);\n            descriptionColumn.setPrefWidth(0);\n\n            // time column\n            ColumnConstraints timeColumn = new ColumnConstraints();\n            timeColumn.setHalignment(HPos.RIGHT);\n\n            gridPane = new GridPane();\n            gridPane.setGridLinesVisible(true);\n            gridPane.setMinWidth(0);\n            gridPane.setPrefWidth(0);\n            gridPane.getStyleClass().add(AGENDA_VIEW_BODY);\n            gridPane.getColumnConstraints().addAll(iconColumn, descriptionColumn, timeColumn);\n\n            return gridPane;\n        }\n\n        /**\n         * Creates the header part for each cell. The header consists of a border pane\n         * with the weekday label in the \"left\" position and the date label in the \"right\"\n         * position.\n         *\n         * @return the header node\n         */\n        protected Node createHeader() {\n            headerPane = new BorderPane();\n            headerPane.getStyleClass().add(AGENDA_VIEW_HEADER);\n            headerPane.setVisible(headerPaneVisible);\n            headerPane.managedProperty().bind(headerPane.visibleProperty());\n\n            weekdayLabel = new Label();\n            weekdayLabel.getStyleClass().add(AGENDA_VIEW_WEEKDAY_LABEL);\n            weekdayLabel.setMinWidth(0);\n\n            dateLabel = new Label();\n            dateLabel.setMinWidth(0);\n            dateLabel.getStyleClass().add(AGENDA_VIEW_DATE_LABEL);\n\n            headerPane.setLeft(weekdayLabel);\n            headerPane.setRight(dateLabel);\n\n            return headerPane;\n        }\n\n        @Override\n        protected void updateItem(AgendaEntry item, boolean empty) {\n            super.updateItem(item, empty);\n            gridPane.getChildren().clear();\n\n            if (item != null) {\n                LocalDate date = item.getDate();\n                if (date.equals(agendaView.getToday())) {\n                    if (!headerPane.getStyleClass().contains(AGENDA_VIEW_HEADER_TODAY)) {\n                        headerPane.getStyleClass().add(AGENDA_VIEW_HEADER_TODAY);\n                        dateLabel.getStyleClass().add(AGENDA_VIEW_DATE_LABEL_TODAY);\n                        weekdayLabel.getStyleClass().add(AGENDA_VIEW_WEEKDAY_LABEL_TODAY);\n                    }\n                } else {\n                    headerPane.getStyleClass().remove(AGENDA_VIEW_HEADER_TODAY);\n                    dateLabel.getStyleClass().remove(AGENDA_VIEW_DATE_LABEL_TODAY);\n                    weekdayLabel.getStyleClass().remove(AGENDA_VIEW_WEEKDAY_LABEL_TODAY);\n                }\n\n                dateLabel.setText(mediumDateFormatter.format(date));\n                weekdayLabel.setText(weekdayFormatter.format(date));\n\n                int count = item.getEntries().size();\n                int row = 0;\n\n                for (int i = 0; i < count; i++) {\n                    Entry<?> entry = item.getEntries().get(i);\n\n                    Node entryGraphic = createEntryGraphic(entry);\n                    gridPane.add(entryGraphic, 0, row);\n\n                    Label titleLabel = createEntryTitleLabel(entry);\n                    titleLabel.setMinWidth(0);\n                    gridPane.add(titleLabel, 1, row);\n\n                    Label timeLabel = createEntryTimeLabel(entry);\n                    timeLabel.setMinWidth(0);\n                    gridPane.add(timeLabel, 2, row);\n\n                    if (count > 1 && i < count - 1) {\n                        Region separator = new Region();\n                        separator.getStyleClass().add(AGENDA_VIEW_BODY_SEPARATOR);\n                        row++;\n                        gridPane.add(separator, 0, row);\n                        GridPane.setColumnSpan(separator, 3);\n                        GridPane.setFillWidth(separator, true);\n                    }\n\n                    row++;\n                }\n                getGraphic().setVisible(true);\n            } else {\n                getGraphic().setVisible(false);\n            }\n        }\n\n        /**\n         * Creates the label used to display the time of the entry. The default implementation of this\n         * method creates a label and sets the text returned by {@link #getTimeText(Entry)}.\n         *\n         * @param entry the entry for which the time will be displayed\n         * @return a label for displaying the time information on the entry\n         */\n        protected Label createEntryTimeLabel(Entry<?> entry) {\n            Label timeLabel = new Label(getTimeText(entry));\n            timeLabel.getStyleClass().add(AGENDA_VIEW_TIME_LABEL);\n            if (agendaView.isEnableHyperlinks()) {\n                timeLabel.setOnMouseClicked(evt -> fireEvent(new RequestEvent(this, this, entry)));\n                timeLabel.getStyleClass().add(\"date-hyperlink\");\n            }\n            return timeLabel;\n        }\n\n        /**\n         * Creates the label used to display the title of the entry. The default implementation of this\n         * method creates a label and sets the text found in {@link Entry#getTitle()}.\n         *\n         * @param entry the entry for which the title will be displayed\n         * @return a label for displaying the title of the entry\n         */\n        protected Label createEntryTitleLabel(Entry<?> entry) {\n            Label titleLabel = new Label(entry.getTitle());\n            titleLabel.getStyleClass().add(AGENDA_VIEW_TITLE_LABEL);\n            if (agendaView.isEnableHyperlinks()) {\n                titleLabel.setOnMouseClicked(evt -> fireEvent(new RequestEvent(this, this, entry)));\n                titleLabel.getStyleClass().add(\"date-hyperlink\");\n            }\n            return titleLabel;\n        }\n\n        /**\n         * Creates a node used to display an icon for the entry. The default implementation of this method\n         * creates a node of type {@link Circle}. The color of the circle will match the color of\n         * the calendar to which the entry belongs.\n         * <pre>\n         * \t  Circle circle = new Circle(4);\n         * \t  circle.getStyleClass().add(entry.getCalendar().getStyle() + \"-icon\");\n         * </pre>\n         *\n         * @param entry the entry for which the icon will be displayed\n         * @return a node for displaying a graphic for the entry\n         */\n        protected Node createEntryGraphic(Entry<?> entry) {\n            Circle circle = new Circle(4);\n            circle.getStyleClass().add(entry.getCalendar().getStyle() + \"-icon\");\n            return circle;\n        }\n\n        /**\n         * Creates a nicely formatted text that contains the start and end time of\n         * the given entry. The text can also be something like \"full day\" if the entry\n         * is a full-day entry.\n         *\n         * @param entry the entry for which the text will be created\n         * @return a text showing the start and end times of the entry\n         */\n        protected String getTimeText(Entry<?> entry) {\n            if (entry.isFullDay()) {\n                return Messages.getString(\"AgendaEntryCell.ALL_DAY\");\n            }\n\n            LocalDate startDate = entry.getStartDate();\n            LocalDate endDate = entry.getEndDate();\n\n            String text;\n\n            if (startDate.equals(endDate)) {\n\n                if (Objects.equals(entry.getZoneId(), agendaView.getZoneId())) {\n                    text = MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE\"),\n                            timeFormatter.format(entry.getStartAsZonedDateTime()), timeFormatter.format(entry.getEndAsZonedDateTime()));\n                } else {\n                    text = MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE\"),\n                            timeFormatter.format(entry.getStartAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())), timeFormatter.format(entry.getEndAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())));\n                    text = text + \" (\" + MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE\"),\n                            timeFormatter.format(entry.getStartAsZonedDateTime()), timeFormatter.format(entry.getEndAsZonedDateTime())) + \" \" +\n                            entry.getZoneId().getDisplayName(TextStyle.SHORT, Locale.getDefault()) +\n                            \")\";\n                }\n            } else {\n\n                if (Objects.equals(entry.getZoneId(), agendaView.getZoneId())) {\n                    text = MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE\"),\n                            shortDateFormatter.format(entry.getStartAsZonedDateTime()), timeFormatter.format(entry.getStartAsZonedDateTime()), shortDateFormatter.format(entry.getEndAsZonedDateTime()),\n                            timeFormatter.format(entry.getEndAsZonedDateTime()));\n                } else {\n                    text = MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE\"),\n                            shortDateFormatter.format(entry.getStartAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())), timeFormatter.format(entry.getStartAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())), shortDateFormatter.format(entry.getEndAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())),\n                            timeFormatter.format(entry.getEndAsZonedDateTime().withZoneSameInstant(agendaView.getZoneId())));\n                    text = text + \"\\n\" + MessageFormat.format(Messages.getString(\"AgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE\"),\n                            shortDateFormatter.format(entry.getStartAsZonedDateTime()), timeFormatter.format(entry.getStartAsZonedDateTime()), shortDateFormatter.format(entry.getEndAsZonedDateTime()),\n                            timeFormatter.format(entry.getEndAsZonedDateTime())) + \" \" + entry.getZoneId().getDisplayName(TextStyle.SHORT, Locale.getDefault());\n\n                }\n            }\n\n            return text;\n        }\n\n        /**\n         * Sets the Week Formatter, the value by default is 'EEEE' Format.\n         *\n         * @param weekdayFormatter sets the week date time format.\n         */\n        public void setWeekdayFormatter(DateTimeFormatter weekdayFormatter) {\n            this.weekdayFormatter = weekdayFormatter;\n        }\n\n        /**\n         * Sets the Medium Date Formatter, the value by default is {@link FormatStyle#MEDIUM}. <br>\n         * Is used to set a format text on the Date Label.\n         *\n         * @param mediumDateFormatter sets medium date time format.\n         */\n        public void setMediumDateFormatter(DateTimeFormatter mediumDateFormatter) {\n            this.mediumDateFormatter = mediumDateFormatter;\n        }\n\n        /**\n         * Sets the Short Date Formatter, the value by default is {@link FormatStyle#SHORT}. <br>\n         * Is be used to set a Date format text in {@link #getTimeText(Entry)}\n         *\n         * @param shortDateFormatter sets the short date time format.\n         */\n        public void setShortDateFormatter(DateTimeFormatter shortDateFormatter) {\n            this.shortDateFormatter = shortDateFormatter;\n        }\n\n        /**\n         * Sets the Time Formatter, the value by default is {@link FormatStyle#SHORT}. <br>\n         * Is used to set a Time format text in {@link #getTimeText(Entry)}\n         *\n         * @param timeFormatter sets the time format.\n         */\n        public void setTimeFormatter(DateTimeFormatter timeFormatter) {\n            this.timeFormatter = timeFormatter;\n        }\n    }\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(lookAheadPeriodInDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setLookAheadPeriodInDays((int) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getLookAheadPeriodInDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Look Ahead Period\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Look ahead period in days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return AGENDA_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(lookBackPeriodInDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setLookBackPeriodInDays((int) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getLookBackPeriodInDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Look Back Period\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Look back period in days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return AGENDA_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showStatusLabelProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowStatusLabel((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowStatusLabel();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Status Label\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Status Label\";\n            }\n\n            @Override\n            public String getCategory() {\n                return AGENDA_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/AllDayEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.AllDayEntryViewSkin;\nimport javafx.scene.control.Skin;\n\n/**\n * An entry view specialized for display in the {@link AllDayView} control.\n */\npublic class AllDayEntryView extends EntryViewBase<AllDayView> {\n\n    /**\n     * Constructs a new entry.\n     *\n     * @param entry the entry for which the view will be created\n     */\n    public AllDayEntryView(Entry<?> entry) {\n        super(entry);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new AllDayEntryViewSkin(this);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/AllDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.AllDayViewSkin;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.css.CssMetaData;\nimport javafx.css.StyleConverter;\nimport javafx.css.Styleable;\nimport javafx.css.StyleableDoubleProperty;\nimport javafx.css.StyleableObjectProperty;\nimport javafx.css.StyleableProperty;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A date control used on top of a {@link DayView} or a {@link DetailedWeekView} for\n * showing \"full day\" calendar entries. This view can be configured to span a\n * given number of days. One day is sufficient when used with a {@link DayView}\n * and seven days when used with a {@link DetailedWeekView}.\n *\n * <img src=\"doc-files/all-day-view.png\" alt=\"All Day View\">\n *\n * @see Entry#isFullDay()\n */\npublic class AllDayView extends DateControl implements ZonedDateTimeProvider {\n\n    private static final String ALL_DAY_VIEW = \"all-day-view\";\n\n    /**\n     * Constructs a new view for the given number of days.\n     *\n     * @param numberOfDays the number of days to be shown by this view\n     */\n    public AllDayView(int numberOfDays) {\n        if (numberOfDays <= 0) {\n            throw new IllegalArgumentException(\"number of days must be larger than zero\");\n        }\n\n        getStyleClass().add(ALL_DAY_VIEW);\n        setNumberOfDays(numberOfDays);\n\n        new CreateAndDeleteHandler(this);\n    }\n\n    /**\n     * Constructs a new view for seven days.\n     */\n    public AllDayView() {\n        this(7);\n    }\n\n    @Override\n    public final ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId) {\n        int day = (int) (x / (getWidth() / getNumberOfDays()));\n\n        LocalDate date = getDate();\n\n        if (isAdjustToFirstDayOfWeek()) {\n            date = Util.adjustToFirstDayOfWeek(date, getFirstDayOfWeek());\n        }\n\n        date = date.plusDays(day);\n\n        LocalTime time = LocalTime.NOON;\n        return ZonedDateTime.of(date, time, zoneId);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new AllDayViewSkin(this);\n    }\n\n    private StyleableObjectProperty<Insets> extraPadding;\n\n    /**\n     * Extra padding to be used inside of the view above and below the full day\n     * entries. This is required as the regular padding is already used for\n     * other styling purposes.\n     *\n     * @return insets for extra padding\n     */\n    public final ObjectProperty<Insets> extraPaddingProperty() {\n        if (extraPadding == null) {\n            extraPadding = new StyleableObjectProperty<>(new Insets(2, 0, 9, 0)) {\n\n                @Override\n                public CssMetaData<AllDayView, Insets> getCssMetaData() {\n                    return StyleableProperties.EXTRA_PADDING;\n                }\n\n                @Override\n                public Object getBean() {\n                    return AllDayView.this;\n                }\n\n                @Override\n                public String getName() {\n                    return \"extraPadding\";\n                }\n            };\n        }\n\n        return extraPadding;\n    }\n\n    /**\n     * Returns the value of {@link #extraPaddingProperty()}.\n     *\n     * @return extra padding insets\n     */\n    public final Insets getExtraPadding() {\n        return extraPaddingProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #extraPaddingProperty()}.\n     *\n     * @param padding padding insets\n     */\n    public final void setExtraPadding(Insets padding) {\n        requireNonNull(padding);\n        extraPaddingProperty().set(padding);\n    }\n\n    private StyleableDoubleProperty rowHeight;\n\n    /**\n     * The height for each row shown by the view. This value determines the\n     * total height of the view.\n     *\n     * @return the row height property\n     */\n    public final DoubleProperty rowHeightProperty() {\n        if (rowHeight == null) {\n            rowHeight = new StyleableDoubleProperty(20) {\n\n                @Override\n                public CssMetaData<AllDayView, Number> getCssMetaData() {\n                    return StyleableProperties.ROW_HEIGHT;\n                }\n\n                @Override\n                public Object getBean() {\n                    return AllDayView.this;\n                }\n\n                @Override\n                public String getName() {\n                    return \"rowHeight\";\n                }\n            };\n        }\n\n        return rowHeight;\n    }\n\n    /**\n     * Returns the value of {@link #rowHeightProperty()}.\n     *\n     * @return the row height\n     */\n    public final double getRowHeight() {\n        return rowHeightProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #rowHeightProperty()}.\n     *\n     * @param height the new row height\n     */\n    public final void setRowHeight(double height) {\n        rowHeightProperty().set(height);\n    }\n\n    private StyleableDoubleProperty rowSpacing;\n\n    /**\n     * Stores the spacing between rows in the view.\n     *\n     * @return the spacing between rows in pixels\n     */\n    public final DoubleProperty rowSpacingProperty() {\n        if (rowSpacing == null) {\n            rowSpacing = new StyleableDoubleProperty(2) {\n\n                @Override\n                public CssMetaData<AllDayView, Number> getCssMetaData() {\n                    return StyleableProperties.ROW_SPACING;\n                }\n\n                @Override\n                public Object getBean() {\n                    return AllDayView.this;\n                }\n\n                @Override\n                public String getName() {\n                    return \"rowSpacing\";\n                }\n            };\n        }\n\n        return rowSpacing;\n    }\n\n    /**\n     * Returns the value of {@link #rowSpacingProperty()}.\n     *\n     * @return the row spacing in pixels\n     */\n    public final double getRowSpacing() {\n        return rowSpacingProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #rowSpacingProperty()}.\n     *\n     * @param space the space between rows in pixel\n     */\n    public final void setRowSpacing(double space) {\n        if (space < 0) {\n            throw new IllegalArgumentException(\"row spacing can not be smaller than zero\");\n        }\n        rowSpacingProperty().set(space);\n    }\n\n    private StyleableDoubleProperty columnSpacing;\n\n    /**\n     * Stores the spacing between columns in the view.\n     *\n     * @return the spacing between columns in pixels\n     */\n    public final DoubleProperty columnSpacingProperty() {\n        if (columnSpacing == null) {\n            columnSpacing = new StyleableDoubleProperty(2) {\n\n                @Override\n                public CssMetaData<AllDayView, Number> getCssMetaData() {\n                    return StyleableProperties.COLUMN_SPACING;\n                }\n\n                @Override\n                public Object getBean() {\n                    return AllDayView.this;\n                }\n\n                @Override\n                public String getName() {\n                    return \"columnSpacing\";\n                }\n            };\n        }\n\n        return columnSpacing;\n    }\n\n    /**\n     * Returns the value of {@link #columnSpacingProperty()}.\n     *\n     * @return the row spacing in pixels\n     */\n    public final double getColumnSpacing() {\n        return columnSpacingProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #columnSpacingProperty()}.\n     *\n     * @param space the space between columns in pixel\n     */\n    public final void setColumnSpacing(double space) {\n        columnSpacingProperty().set(space);\n    }\n\n    private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, \"adjustToFirstDayOfWeek\", true);\n\n    /**\n     * A flag used to indicate that the view should always show the first day of\n     * the week (e.g. \"Monday\") at its beginning even if the\n     * {@link #dateProperty()} is set to another day (e.g. \"Thursday\"). The\n     * adjustment is normally needed if the view is used in combination with the\n     * {@link DetailedWeekView}. It is not needed if the view is used together with the\n     * {@link DayView}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final BooleanProperty adjustToFirstDayOfWeekProperty() {\n        return adjustToFirstDayOfWeek;\n    }\n\n    /**\n     * Returns the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final boolean isAdjustToFirstDayOfWeek() {\n        return adjustToFirstDayOfWeekProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @param adjust if true the view will always show the first day of the week\n     */\n    public final void setAdjustToFirstDayOfWeek(boolean adjust) {\n        adjustToFirstDayOfWeekProperty().set(adjust);\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\");\n\n    /**\n     * Stores the number of days that will be shown by this view. This value\n     * will be 1 if the view is used in combination with the {@link DayView} and\n     * 7 if used together with the {@link DetailedWeekView}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \" + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n\n    private final ObjectProperty<Callback<Entry<?>, AllDayEntryView>> entryViewFactory = new SimpleObjectProperty<>(this, \"entryViewFactory\", AllDayEntryView::new);\n\n    /**\n     * A callback used for producing views for entries. The views have to be of\n     * type {@link AllDayEntryView}.\n     *\n     * @return the entry view factory\n     */\n    public final ObjectProperty<Callback<Entry<?>, AllDayEntryView>> entryViewFactoryProperty() {\n        return entryViewFactory;\n    }\n\n    /**\n     * Returns the value of {@link #entryViewFactoryProperty()}.\n     *\n     * @return the entry view factory callback\n     */\n    public final Callback<Entry<?>, AllDayEntryView> getEntryViewFactory() {\n        return entryViewFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #entryViewFactoryProperty()}.\n     *\n     * @param factory the new entry view factory\n     */\n    public final void setEntryViewFactory(Callback<Entry<?>, AllDayEntryView> factory) {\n        requireNonNull(factory);\n        entryViewFactoryProperty().set(factory);\n    }\n\n    private final ObjectProperty<Callback<AllDayView, Region>> separatorFactory = new SimpleObjectProperty<>(this, \"separatorFactory\", it -> {\n        Region region = new Region();\n        region.getStyleClass().add(\"weekday-separator\");\n        return region;\n    });\n\n\n    public final Callback<AllDayView, Region> getSeparatorFactory() {\n        return separatorFactory.get();\n    }\n\n    /**\n     * A factory used for creating (optional) vertical separators between the all day view.\n     *\n     * @return the separator factory\n     */\n    public final ObjectProperty<Callback<AllDayView, Region>> separatorFactoryProperty() {\n        return separatorFactory;\n    }\n\n    public final void setSeparatorFactory(Callback<AllDayView, Region> separatorFactory) {\n        this.separatorFactory.set(separatorFactory);\n    }\n\n    private static class StyleableProperties {\n\n        private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;\n\n        private static final CssMetaData<AllDayView, Number> ROW_HEIGHT = new CssMetaData<AllDayView, Number>(\n                \"-fx-row-height\", StyleConverter.getSizeConverter(), 20d) {\n\n            @Override\n            public Double getInitialValue(AllDayView node) {\n                return node.getRowHeight();\n            }\n\n            @Override\n            public boolean isSettable(AllDayView n) {\n                return n.rowHeight == null || !n.rowHeight.isBound();\n            }\n\n            @SuppressWarnings(\"unchecked\")\n            @Override\n            public StyleableProperty<Number> getStyleableProperty(AllDayView n) {\n                return (StyleableProperty<Number>) n.rowHeightProperty();\n            }\n        };\n\n        private static final CssMetaData<AllDayView, Number> ROW_SPACING = new CssMetaData<AllDayView, Number>(\n                \"-fx-row-spacing\", StyleConverter.getSizeConverter(), 2d) {\n\n            @Override\n            public Double getInitialValue(AllDayView node) {\n                return node.getRowSpacing();\n            }\n\n            @Override\n            public boolean isSettable(AllDayView n) {\n                return n.rowSpacing == null || !n.rowSpacing.isBound();\n            }\n\n            @SuppressWarnings(\"unchecked\")\n            @Override\n            public StyleableProperty<Number> getStyleableProperty(AllDayView n) {\n                return (StyleableProperty<Number>) n.rowSpacingProperty();\n            }\n        };\n\n        private static final CssMetaData<AllDayView, Number> COLUMN_SPACING = new CssMetaData<AllDayView, Number>(\n                \"-fx-column-spacing\", StyleConverter.getSizeConverter(), 2d) {\n\n            @Override\n            public Double getInitialValue(AllDayView node) {\n                return node.getColumnSpacing();\n            }\n\n            @Override\n            public boolean isSettable(AllDayView n) {\n                return n.columnSpacing == null || !n.columnSpacing.isBound();\n            }\n\n            @SuppressWarnings(\"unchecked\")\n            @Override\n            public StyleableProperty<Number> getStyleableProperty(AllDayView n) {\n                return (StyleableProperty<Number>) n.columnSpacingProperty();\n            }\n        };\n\n        private static final CssMetaData<AllDayView, Insets> EXTRA_PADDING = new CssMetaData<AllDayView, Insets>(\n                \"-fx-extra-padding\", StyleConverter.getInsetsConverter(),\n                Insets.EMPTY) {\n\n            @Override\n            public Insets getInitialValue(AllDayView node) {\n                return node.getExtraPadding();\n            }\n\n            @Override\n            public boolean isSettable(AllDayView n) {\n                return n.extraPadding == null || !n.extraPadding.isBound();\n            }\n\n            @SuppressWarnings(\"unchecked\")\n            @Override\n            public StyleableProperty<Insets> getStyleableProperty(AllDayView n) {\n                return (StyleableProperty<Insets>) n.extraPaddingProperty();\n            }\n        };\n\n        static {\n            final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Region.getClassCssMetaData());\n\n            styleables.add(ROW_HEIGHT);\n            styleables.add(ROW_SPACING);\n            styleables.add(COLUMN_SPACING);\n            styleables.add(EXTRA_PADDING);\n\n            STYLEABLES = Collections.unmodifiableList(styleables);\n        }\n    }\n\n    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {\n        return StyleableProperties.STYLEABLES;\n    }\n\n    @Override\n    public final List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {\n        return getClassCssMetaData();\n    }\n\n    private static final String ALL_DAY_VIEW_CATEGORY = \"All Day View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(numberOfDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setNumberOfDays((Integer) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getNumberOfDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Number Of Days\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Determines how many days will be covered by this control\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ALL_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        // column spacing\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(columnSpacingProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setColumnSpacing((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getColumnSpacing();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Column Spacing\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The gap between the days / columns\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ALL_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        // row height\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(rowHeightProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setRowHeight((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getRowHeight();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Row Height\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The height of each row in the control\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ALL_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        // row spacing\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(rowSpacingProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setRowSpacing((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getRowSpacing();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Row Spacing\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The gap between the rows\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ALL_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        // extra padding\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(extraPaddingProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setExtraPadding((Insets) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getExtraPadding();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Insets.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Extra Padding\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Additional padding inside the control\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ALL_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/ButtonBar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.ButtonBarSkin;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Skin;\n\n/**\n * A segmented button bar control based on the segmented button control\n * found in ControlsFX but this one working with regular buttons and not\n * toggle buttons.\n * <img src=\"doc-files/button-bar.png\" alt=\"Button Bar\">\n */\npublic class ButtonBar extends CalendarFXControl {\n\n    private final ObservableList<Button> buttons;\n\n    /**\n     * Constructs a new button bar.\n     */\n    public ButtonBar() {\n        this((ObservableList<Button>) null);\n    }\n\n    /**\n     * Constructs a new button bar for the given buttons.\n     *\n     * @param buttons the buttons to add to the control\n     */\n    public ButtonBar(Button... buttons) {\n        this(buttons == null ? FXCollections.observableArrayList() : FXCollections.observableArrayList(buttons));\n    }\n\n    /**\n     * Constructs a new button bar for the given buttons.\n     *\n     * @param buttons the buttons to add to the control\n     */\n    public ButtonBar(ObservableList<Button> buttons) {\n        this.getStyleClass().add(\"button-bar\");\n        this.buttons = buttons == null ? FXCollections.observableArrayList() : buttons;\n        this.setFocusTraversable(false);\n    }\n\n    protected Skin<?> createDefaultSkin() {\n        return new ButtonBarSkin(this);\n    }\n\n    /**\n     * The list of buttons managed by this button bar.\n     *\n     * @return the list of buttons managed by the control\n     */\n    public final ObservableList<Button> getButtons() {\n        return this.buttons;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/CalendarFXControl.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Control;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.util.Objects;\n\n/**\n * Common superclass for all controls in the calendar framework.\n */\npublic abstract class CalendarFXControl extends Control {\n\n    private static String stylesheet;\n\n    /**\n     * Constructs a new control.\n     */\n    protected CalendarFXControl() {\n    }\n\n    @Override\n    public final String getUserAgentStylesheet() {\n        if (stylesheet == null) {\n            if (Boolean.getBoolean(\"atlantafx\")) {\n                stylesheet = Objects.requireNonNull(CalendarFXControl.class.getResource(\"atlantafx.css\")).toExternalForm();\n            } else {\n                stylesheet = Objects.requireNonNull(CalendarFXControl.class.getResource(\"calendar.css\")).toExternalForm();\n            }\n        }\n        return stylesheet;\n    }\n\n    /**\n     * Returns a list of property items that can be shown by the\n     * {@link PropertySheet} of ControlsFX.\n     *\n     * @return the property sheet items\n     */\n    public ObservableList<Item> getPropertySheetItems() {\n        return FXCollections.observableArrayList();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/CalendarHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.DateControl.Layout;\nimport impl.com.calendarfx.view.CalendarHeaderViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A view used for showing the short names of calendars on top of those\n * {@link DayView} instances that are using the {@link Layout#SWIMLANE} layout\n * strategy.\n *\n * <img width=\"100%\" src=\"doc-files/calendar-header-view.png\" alt=\"Calendar Header View\">\n *\n * @see DetailedWeekView#getCalendarHeaderView()\n */\npublic class CalendarHeaderView extends CalendarFXControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"calendar-header\";\n\n    /**\n     * Constructs a new calendar header view.\n     */\n    public CalendarHeaderView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        setCellFactory(calendar -> {\n            Label label = new Label();\n            label.textProperty().bind(calendar.shortNameProperty());\n            label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n            return label;\n        });\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new CalendarHeaderViewSkin(this);\n    }\n\n    public final void bind(DateControl dateControl) {\n        Bindings.bindContentBidirectional(calendars, dateControl.getCalendars());\n        Bindings.bindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n    }\n\n    public final void unbind(DateControl dateControl) {\n        // important, otherwise we end up with a memory leak\n        getCalendarVisibilityMap().clear();\n\n        Bindings.unbindContentBidirectional(calendars, dateControl.getCalendars());\n        Bindings.unbindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n    }\n\n    private final ObservableMap<Calendar, BooleanProperty> calendarVisibilityMap = FXCollections.observableHashMap();\n\n    public final ObservableMap<Calendar, BooleanProperty> getCalendarVisibilityMap() {\n        return calendarVisibilityMap;\n    }\n\n    public final BooleanProperty getCalendarVisibilityProperty(Calendar calendar) {\n        return calendarVisibilityMap.computeIfAbsent(calendar, cal -> new SimpleBooleanProperty(CalendarHeaderView.this, \"visible\", true));\n    }\n\n    public final boolean isCalendarVisible(Calendar calendar) {\n        BooleanProperty prop = getCalendarVisibilityProperty(calendar);\n        return prop.get();\n    }\n\n    public final void setCalendarVisibility(Calendar calendar, boolean visible) {\n        BooleanProperty prop = getCalendarVisibilityProperty(calendar);\n        prop.set(visible);\n    }\n\n    private final ObjectProperty<Callback<Calendar, Node>> cellFactory = new SimpleObjectProperty<>(this, \"cellFactory\");\n\n    /**\n     * Returns the property used for storing a reference to a cell factory that\n     * will be used to create the nodes that will serve as column headers.\n     *\n     * @return the cell factory property\n     */\n    public final ObjectProperty<Callback<Calendar, Node>> cellFactoryProperty() {\n        return cellFactory;\n    }\n\n    /**\n     * Sets the value of {@link #cellFactoryProperty()}.\n     *\n     * @param factory\n     *            the factory\n     */\n    public final void setCellFactory(Callback<Calendar, Node> factory) {\n        requireNonNull(factory);\n        cellFactoryProperty().set(factory);\n    }\n\n    /**\n     * Returns the value of {@link #cellFactoryProperty()}.\n     *\n     * @return the cell factory\n     */\n    public final Callback<Calendar, Node> getCellFactory() {\n        return cellFactory.get();\n    }\n\n    private final ObservableList<Calendar> calendars = FXCollections.observableArrayList();\n\n    /**\n     * The list of calendars for which the view will display a header.\n     *\n     * @return the calendars\n     */\n    public final ObservableList<Calendar> getCalendars() {\n        return calendars;\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\", 1);\n\n    /**\n     * Stores the number of days that will be shown by this view. This value\n     * will be 1 if the view is used in combination with the {@link DayView} and\n     * 7 if used together with the {@link DetailedWeekView}.\n     *\n     * @return the number of days shown by the view\n     *\n     * @see DetailedWeekView#numberOfDaysProperty()\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number\n     *            the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \"\n                    + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/CalendarSelector.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport impl.com.calendarfx.view.CalendarSelectorSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\n\n/**\n * A control for choosing a calendar from a list of calendars.\n *\n * <img src=\"doc-files/calendar-selector.png\" alt=\"Calendar Selector\">\n */\npublic class CalendarSelector extends CalendarFXControl {\n\n    /**\n     * Constructs a new selector.\n     */\n    public CalendarSelector() {\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new CalendarSelectorSkin(this);\n    }\n\n    private final ObservableList<Calendar> calendars = FXCollections.observableArrayList();\n\n    /**\n     * The available list of calendars from which the user can select.\n     *\n     * @return the list of calendars\n     */\n    public final ObservableList<Calendar> getCalendars() {\n        return calendars;\n    }\n\n    /*\n     * Support for value.\n     */\n    private final ObjectProperty<Calendar> calendar = new SimpleObjectProperty<>(this, \"calendar\");\n\n    /**\n     * A property used to store the current value of the control, the currently\n     * selected calendar.\n     *\n     * @return the selected calendar\n     */\n    public final ObjectProperty<Calendar> calendarProperty() {\n        return calendar;\n    }\n\n    /**\n     * Sets the value of {@link #calendarProperty()}.\n     *\n     * @param calendar the selected calendar\n     */\n    public final void setCalendar(Calendar calendar) {\n        calendarProperty().set(calendar);\n    }\n\n    /**\n     * Returns the value of {@link #calendarProperty()}.\n     *\n     * @return the currently selected calendar\n     */\n    public final Calendar getCalendar() {\n        return calendarProperty().get();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/CalendarView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.view.page.DayPage;\nimport com.calendarfx.view.page.MonthPage;\nimport com.calendarfx.view.page.PageBase;\nimport com.calendarfx.view.page.WeekPage;\nimport com.calendarfx.view.page.YearPage;\nimport com.calendarfx.view.print.PrintView;\nimport com.dlsc.gemsfx.SearchTextField;\nimport impl.com.calendarfx.view.CalendarViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.MapChangeListener.Change;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.BorderPane;\nimport org.controlsfx.control.PropertySheet.Item;\nimport org.controlsfx.control.textfield.CustomTextField;\nimport org.controlsfx.control.textfield.TextFields;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static com.calendarfx.view.CalendarView.Page.DAY;\nimport static com.calendarfx.view.CalendarView.Page.MONTH;\nimport static com.calendarfx.view.CalendarView.Page.WEEK;\nimport static com.calendarfx.view.CalendarView.Page.YEAR;\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A full calendar view with multiple pages for displaying a single day, a week,\n * a month, and an entire year. The view also includes two trays, one for seeing\n * all calendar sources, and one for seeing the results of the current search.\n * The trays can be shown or hidden. Another nice feature is direct support for\n * printing (see {@link #getPrintView()}).\n *\n * <img width=\"100%\" src=\"doc-files/calendar-view.png\" alt=\"Calendar View\">\n */\npublic class CalendarView extends DateControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"calendar-view\";\n\n    private static final String SELECTED_PAGE = \"com.calendarfx.selectedPage\";\n\n    private final SourceView sourceView;\n\n    private final SearchResultView searchResultView;\n\n    private final YearMonthView yearMonthView;\n\n    private DayPage dayPage;\n\n    private WeekPage weekPage;\n\n    private MonthPage monthPage;\n\n    private YearPage yearPage;\n\n    private DeveloperConsole developerConsole;\n\n    private final SearchTextField searchField;\n\n    private PrintView printView;\n\n    public CalendarView() {\n        this(Page.values());\n    }\n\n    /**\n     * Constructs a new calendar view.\n     */\n    public CalendarView(Page... availablePages) {\n        Objects.requireNonNull(availablePages, \"available pages can not be null\");\n\n        if (availablePages.length == 0) {\n            throw new IllegalArgumentException(\"no available page passed to constructor\");\n        }\n\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        getAvailablePages().setAll(availablePages);\n\n        this.searchField = new SearchTextField();\n        this.sourceView = new SourceView();\n        this.searchResultView = new SearchResultView();\n        this.yearMonthView = new YearMonthView();\n\n        if (Boolean.getBoolean(\"calendarfx.developer\")) {\n            this.developerConsole = new DeveloperConsole();\n            this.developerConsole.setDateControl(this);\n        }\n\n        selectedPage.set(availablePages[0]);\n\n        Bindings.bindBidirectional(searchField.visibleProperty(), showSearchFieldProperty());\n\n        /*\n         * We are \"abusing\" the properties map to pass new values of read-only\n         * properties from the skin to the control.\n         */\n        getProperties().addListener((Change<?, ?> change) -> {\n            if (change.getKey().equals(SELECTED_PAGE)) {\n                if (change.getValueAdded() != null) {\n                    Page page = (Page) change.getValueAdded();\n                    selectedPage.set(page);\n                    getProperties().remove(SELECTED_PAGE);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new CalendarViewSkin(this);\n    }\n\n    public PageBase getPageView(Page page) {\n        switch (page) {\n            case DAY:\n                return getDayPage();\n            case WEEK:\n                return getWeekPage();\n            case MONTH:\n                return getMonthPage();\n            case YEAR:\n                return getYearPage();\n            default:\n                throw new IllegalArgumentException(\"unknown page: \" + page);\n        }\n    }\n\n    /**\n     * An enumerator listing the available pages that can be shown\n     * by the calendar view. By default, the CalendarView contains\n     * all possible pages. Use {@link #getAvailablePages()} to change\n     * this.\n     *\n     * @see #getAvailablePages()\n     */\n    public enum Page {\n        DAY,\n        WEEK,\n        MONTH,\n        YEAR\n    }\n\n    private final ObservableList<Page> availablePages = FXCollections.observableArrayList();\n\n    /**\n     * Returns the list of pages that will be available within the calendar view (day, week,\n     * month, year).\n     *\n     * @return the available pages\n     */\n    public final ObservableList<Page> getAvailablePages() {\n        return availablePages;\n    }\n\n    /**\n     * Returns the developer console that can be made visible via a META-D keystroke\n     * when the system property \"calendarfx.developer\" is set to true.\n     *\n     * @return the developer console or null if the system property\n     * \"calendarfx.developer\" is not set to true\n     */\n    public final DeveloperConsole getDeveloperConsole() {\n        return developerConsole;\n    }\n\n    /**\n     * Returns the day page.\n     *\n     * @return the day page\n     */\n    public final DayPage getDayPage() {\n        if (dayPage == null) {\n            dayPage = new DayPage();\n            bind(dayPage, true);\n        }\n        return dayPage;\n    }\n\n    /**\n     * Returns the week page.\n     *\n     * @return the week page\n     */\n    public final WeekPage getWeekPage() {\n        if (weekPage == null) {\n            weekPage = new WeekPage();\n            bind(weekPage, true);\n        }\n        return weekPage;\n    }\n\n    /**\n     * Returns the month page.\n     *\n     * @return the month page\n     */\n    public final MonthPage getMonthPage() {\n        if (monthPage == null) {\n            monthPage = new MonthPage();\n            bind(monthPage, true);\n        }\n        return monthPage;\n    }\n\n    /**\n     * Returns the year page.\n     *\n     * @return the year page\n     */\n    public final YearPage getYearPage() {\n        if (yearPage == null) {\n            yearPage = new YearPage();\n            bind(yearPage, true);\n        }\n        return yearPage;\n    }\n\n    /**\n     * Returns the search text field.\n     *\n     * @return the search field\n     */\n    public final SearchTextField getSearchField() {\n        return searchField;\n    }\n\n    /**\n     * Returns the search result view child control.\n     *\n     * @return the search result view\n     */\n    public final SearchResultView getSearchResultView() {\n        return searchResultView;\n    }\n\n    /**\n     * Returns the source view child control.\n     *\n     * @return the source view\n     */\n    public final SourceView getSourceView() {\n        return sourceView;\n    }\n\n    /**\n     * Returns the year month view child control.\n     *\n     * @return the year month view\n     */\n    public final YearMonthView getYearMonthView() {\n        return yearMonthView;\n    }\n\n    /**\n     * Returns the print view associated with this view. The print view shows a print\n     * result preview dialog that allows the user to customize several printing options\n     * and trigger a print job to be executed.\n     *\n     * @return the print view\n     */\n    public final PrintView getPrintView() {\n        if (printView == null) {\n            printView = new PrintView();\n        }\n\n        return printView;\n    }\n\n    private final BooleanProperty showDevoloperConsole = new SimpleBooleanProperty(this, \"showDevoloperConsole\", false);\n\n    /**\n     * Controls the visibility of the developer console. The console displays\n     * various types of events that are fired by the calendar while the user is\n     * using it.\n     *\n     * @return true if the developer console will be shown to the user\n     */\n    public final BooleanProperty showDeveloperConsoleProperty() {\n        return showDevoloperConsole;\n    }\n\n    /**\n     * Sets the value of {@link #showDeveloperConsoleProperty()}.\n     *\n     * @param show if true will show the developer console\n     */\n    public final void setShowDeveloperConsole(boolean show) {\n        showDeveloperConsoleProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showSourceTrayProperty()}.\n     *\n     * @return true if the developer console gets shown to the user\n     */\n    public final boolean isShowDeveloperConsole() {\n        return showDeveloperConsoleProperty().get();\n    }\n\n    private final BooleanProperty showSourceTray = new SimpleBooleanProperty(this, \"showSourceTray\", false);\n\n    /**\n     * Controls the visibility of the source tray.\n     *\n     * @return true if the source tray will be shown to the user\n     */\n    public final BooleanProperty showSourceTrayProperty() {\n        return showSourceTray;\n    }\n\n    /**\n     * Sets the value of {@link #showSourceTrayProperty()}.\n     *\n     * @param show if true will show the tray\n     */\n    public final void setShowSourceTray(boolean show) {\n        showSourceTray.set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showSourceTrayProperty()}.\n     *\n     * @return true if the calendar source tray gets shown to the user\n     */\n    public final boolean isShowSourceTray() {\n        return showSourceTray.get();\n    }\n\n    private final BooleanProperty showSearchResultsTray = new SimpleBooleanProperty(this, \"showSearchResultsTray\", false);\n\n    /**\n     * Controls the visibility of the search results tray.\n     *\n     * @return the search result tray visibility\n     */\n    public final BooleanProperty showSearchResultsTrayProperty() {\n        return showSearchResultsTray;\n    }\n\n    /**\n     * Sets the value of {@link #showSearchResultsTrayProperty()}.\n     *\n     * @param show if true the search result tray will be shown to the user\n     */\n    public final void setShowSearchResultsTray(boolean show) {\n        showSearchResultsTray.set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showSearchResultsTrayProperty()}.\n     *\n     * @return true if the search result tray is visible\n     */\n    public final boolean isShowSearchResultsTray() {\n        return showSearchResultsTray.get();\n    }\n\n    private final ReadOnlyObjectWrapper<Page> selectedPage = new ReadOnlyObjectWrapper<>(this, \"selectedPage\");\n\n    /**\n     * A read-only property used for storing the currently selected page.\n     *\n     * @return the selected page view\n     */\n    public final ReadOnlyObjectProperty<Page> selectedPageProperty() {\n        return selectedPage.getReadOnlyProperty();\n    }\n\n    /**\n     * The value of {@link #selectedPageProperty()}.\n     *\n     * @return the selected page view\n     */\n    public final Page getSelectedPage() {\n        return selectedPageProperty().get();\n    }\n\n    /**\n     * Returns the view for the currently selected {@link Page}.\n     *\n     * @return the selected page view\n     * @see #selectedPageProperty()\n     */\n    public final PageBase getSelectedPageView() {\n        return getPageView(getSelectedPage());\n    }\n\n    private final ObjectProperty<Node> header = new SimpleObjectProperty<>(this, \"header\", null);\n\n    /**\n     * Property used to reference a node that can be used as a header for the\n     * calendar view. The skin of this view uses a {@link BorderPane} to lay out\n     * its children. The header will be placed in its top location.\n     *\n     * @return a node added as a header to the view (e.g. a tool bar)\n     */\n    public final ObjectProperty<Node> headerProperty() {\n        return header;\n    }\n\n    /**\n     * Returns the value of {@link #headerProperty()}.\n     *\n     * @return the node that will be shown above the actual calendar\n     */\n    public final Node getHeader() {\n        return headerProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #headerProperty()}.\n     *\n     * @param node a node that will be shown above the actual calendar\n     */\n    public final void setHeader(Node node) {\n        headerProperty().set(node);\n    }\n\n    private final ObjectProperty<Node> footer = new SimpleObjectProperty<>(this, \"footer\", null);\n\n    /**\n     * Property used to reference a node that can be used as a footer for the\n     * calendar view. The skin of this view uses a {@link BorderPane} to lay out\n     * its children. The footer will be placed in its bottom location.\n     *\n     * @return a node added as a footer to the view (e.g. a status bar)\n     */\n    public final ObjectProperty<Node> footerProperty() {\n        return footer;\n    }\n\n    /**\n     * Returns the value of {@link #footerProperty()}.\n     *\n     * @return the header node shown below the actual calendar\n     */\n    public final Node getFooter() {\n        return footerProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #footerProperty()}.\n     *\n     * @param node a node that will be shown below the actual calendar\n     */\n    public final void setFooter(Node node) {\n        footerProperty().set(node);\n    }\n\n    // tray animation support\n\n    private final BooleanProperty traysAnimated = new SimpleBooleanProperty(this, \"traysAnimated\", true);\n\n    /**\n     * A property used to control whether closing or opening the trays (source view,\n     * search result view) will be animated.\n     *\n     * @return true if animations (eye candy) are enabled\n     */\n    public final BooleanProperty traysAnimatedProperty() {\n        return this.traysAnimated;\n    }\n\n    /**\n     * Returns the value of {@link #traysAnimatedProperty()}.\n     *\n     * @return true if tray animations are enabled\n     */\n    public final boolean isTraysAnimated() {\n        return traysAnimatedProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #traysAnimatedProperty()}.\n     *\n     * @param animated if true tray animations will be used when opening / closing\n     *                 the source tray or the search result tray\n     */\n    public final void setTraysAnimated(boolean animated) {\n        traysAnimatedProperty().set(animated);\n    }\n\n    // show search field support\n\n    private final BooleanProperty showSearchField = new SimpleBooleanProperty(this, \"showSearchField\", true);\n\n    /**\n     * Controls whether the search field (text field) in the upper right corner\n     * of the control will be shown to the user or not.\n     *\n     * @return true if the search field will be accessible by the user\n     */\n    public final BooleanProperty showSearchFieldProperty() {\n        return showSearchField;\n    }\n\n    /**\n     * Returns the value of {@link #showSearchFieldProperty()}.\n     *\n     * @return true if the search field will be accessible by the user\n     */\n    public final boolean isShowSearchField() {\n        return showSearchField.get();\n    }\n\n    /**\n     * Sets the value of {@link #showSearchFieldProperty()}.\n     *\n     * @param show if true the search field will be accessible by the user\n     */\n    public final void setShowSearchField(boolean show) {\n        showSearchField.set(show);\n    }\n\n    private final BooleanProperty showSourceTrayButton = new SimpleBooleanProperty(this, \"showSourceTrayButton\", true);\n\n    /**\n     * Controls whether the source tray button in the upper left corner of the\n     * control will be shown to the user or not.\n     *\n     * @return true if the source tray button will be accessible by the user\n     */\n    public final BooleanProperty showSourceTrayButtonProperty() {\n        return showSourceTrayButton;\n    }\n\n    /**\n     * Returns the value of {@link #showSourceTrayButtonProperty()}.\n     *\n     * @return true if the source tray button will be accessible by the user\n     */\n    public final boolean isShowSourceTrayButton() {\n        return showSourceTrayButton.get();\n    }\n\n    /**\n     * Sets the value of {@link #showSourceTrayButtonProperty()}.\n     *\n     * @param show if true the source tray button will be accessible by the user\n     */\n    public final void setShowSourceTrayButton(boolean show) {\n        showSourceTrayButton.set(show);\n    }\n\n    private final BooleanProperty showAddCalendarButton = new SimpleBooleanProperty(this, \"showAddCalendarButton\", true);\n\n    /**\n     * Controls whether the \"add calendar\" button in the upper left corner of\n     * the control will be shown to the user or not.\n     *\n     * @return true if the \"add calendar\" button will be accessible by the user\n     */\n    public final BooleanProperty showAddCalendarButtonProperty() {\n        return showAddCalendarButton;\n    }\n\n    /**\n     * Returns the value of {@link #showAddCalendarButtonProperty()}.\n     *\n     * @return true if the \"add calendar\" button will be accessible by the user\n     */\n    public final boolean isShowAddCalendarButton() {\n        return showAddCalendarButton.get();\n    }\n\n    /**\n     * Sets the value of {@link #showAddCalendarButtonProperty()}.\n     *\n     * @param show if true the \"add calendar\" button will be accessible by the\n     *             user\n     */\n    public final void setShowAddCalendarButton(boolean show) {\n        showAddCalendarButton.set(show);\n    }\n\n    private final BooleanProperty showPrintButton = new SimpleBooleanProperty(this, \"showPrintButton\", true);\n\n    /**\n     * Controls whether the \"print\" button in the upper left corner of the\n     * control will be shown to the user or not.\n     *\n     * @return true if the \"print\" button will be accessible by the user\n     */\n    public final BooleanProperty showPrintButtonProperty() {\n        return showPrintButton;\n    }\n\n    /**\n     * Returns the value of {@link #showPrintButtonProperty()}.\n     *\n     * @return true if the \"print\" button will be accessible by the user\n     */\n    public final boolean isShowPrintButton() {\n        return showPrintButton.get();\n    }\n\n    /**\n     * Sets the value of {@link #showPrintButtonProperty()}.\n     *\n     * @param show if true the \"print\" button will be accessible by the user\n     */\n    public final void setShowPrintButton(boolean show) {\n        showPrintButton.set(show);\n    }\n\n    private final BooleanProperty showPageToolBarControls = new SimpleBooleanProperty(this, \"showPageToolBarControls\", true);\n\n    /**\n     * Controls whether the \"page-specific\" toolbar controls (e.g. DayPage:\n     * agenda view, day view, combined view) in the upper left corner of the\n     * control will be shown to the user or not.\n     *\n     * @return true if the \"page-specific\" toolbar controls will be accessible\n     * by the user\n     * @see PageBase#getToolBarControls()\n     */\n    public final BooleanProperty showPageToolBarControlsProperty() {\n        return showPageToolBarControls;\n    }\n\n    /**\n     * Returns the value of {@link #showPageToolBarControlsProperty()}.\n     *\n     * @return true if the \"page-specific\" toolbar controls will be accessible\n     * by the user\n     * @see PageBase#getToolBarControls()\n     */\n    public final boolean isShowPageToolBarControls() {\n        return showPageToolBarControls.get();\n    }\n\n    /**\n     * Sets the value of {@link #showPageToolBarControlsProperty()}.\n     *\n     * @param show if true the \"page-specific\" toolbar controls will be\n     *             accessible by the user\n     * @see PageBase#getToolBarControls()\n     */\n    public final void setShowPageToolBarControls(boolean show) {\n        showPageToolBarControls.set(show);\n    }\n\n    private final BooleanProperty showPageSwitcher = new SimpleBooleanProperty(this, \"showPageSwitcher\", true);\n\n    /**\n     * Controls whether the page switcher (day, week, month, year) will be shown\n     * to the user or not.\n     *\n     * @return true if the toolbar will be shown to the user\n     */\n    public final BooleanProperty showPageSwitcherProperty() {\n        return showPageSwitcher;\n    }\n\n    /**\n     * Returns the value of {@link #showPageSwitcherProperty()}.\n     *\n     * @return true if the page switcher will be visible\n     */\n    public final boolean isShowPageSwitcher() {\n        return showPageSwitcher.get();\n    }\n\n    /**\n     * Sets the value of {@link #showPageSwitcherProperty()}.\n     *\n     * @param show true if the page switcher will be visible\n     */\n    public final void setShowPageSwitcher(boolean show) {\n        showPageSwitcher.set(show);\n    }\n\n    private final BooleanProperty showToolBar = new SimpleBooleanProperty(this, \"showToolBar\", true);\n\n    /**\n     * Controls whether the toolbar will be shown to the user or not.\n     *\n     * @return true if the toolbar will be shown to the user\n     */\n    public final BooleanProperty showToolBarProperty() {\n        return showToolBar;\n    }\n\n    /**\n     * Returns the value of {@link #showToolBarProperty()}.\n     *\n     * @return true if the toolbar will be visible\n     */\n    public final boolean isShowToolBar() {\n        return showToolBar.get();\n    }\n\n    /**\n     * Sets the value of {@link #showToolBarProperty()}.\n     *\n     * @param show true if the toolbar will be visible\n     */\n    public final void setShowToolBar(boolean show) {\n        showToolBar.set(show);\n    }\n\n    /**\n     * Switches the view to the {@link DayPage}.\n     */\n    public final void showDayPage() {\n        if (getAvailablePages().contains(DAY)) {\n            selectedPage.set(DAY);\n        } else {\n            throw new UnsupportedOperationException(\"calendar does not support day view\");\n        }\n    }\n\n    /**\n     * Switches the view to the {@link WeekPage}.\n     */\n    public final void showWeekPage() {\n        if (getAvailablePages().contains(WEEK)) {\n            selectedPage.set(WEEK);\n        } else {\n            throw new UnsupportedOperationException(\"calendar does not support week view\");\n        }\n    }\n\n    /**\n     * Switches the view to the {@link MonthPage}.\n     */\n    public final void showMonthPage() {\n        if (getAvailablePages().contains(MONTH)) {\n            selectedPage.set(MONTH);\n        } else {\n            throw new UnsupportedOperationException(\"calendar does not support month view\");\n        }\n    }\n\n    /**\n     * Switches the view to the {@link YearPage}.\n     */\n    public final void showYearPage() {\n        if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n        } else {\n            throw new UnsupportedOperationException(\"calendar does not support year view\");\n        }\n    }\n\n    /**\n     * Sends the request to the calendar view to display the given date. The\n     * view will switch to the {@link DayPage} and set the value of\n     * {@link #dateProperty()} to the date.\n     *\n     * @param date the date to show in the view\n     */\n    public final void showDate(LocalDate date) {\n        requireNonNull(date);\n        if (getAvailablePages().contains(DAY)) {\n            selectedPage.set(DAY);\n        } else if (getAvailablePages().contains(WEEK)) {\n            selectedPage.set(WEEK);\n        } else if (getAvailablePages().contains(MONTH)) {\n            selectedPage.set(MONTH);\n        } else if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n        }\n\n        setDate(date);\n    }\n\n    /**\n     * Sends the request to the calendar view to display the given week. The\n     * view will try to switch to the {@link WeekPage} and set the value of\n     * {@link #dateProperty()} to the date.\n     *\n     * @param year       the date to show in the view\n     * @param weekOfYear the week to show in the view\n     */\n    public final void showWeek(Year year, int weekOfYear) {\n        requireNonNull(year);\n        if (weekOfYear < 1) {\n            throw new IllegalArgumentException(\"illegal value for week of year: \" + weekOfYear);\n        }\n        if (getAvailablePages().contains(WEEK)) {\n            selectedPage.set(WEEK);\n        } else if (getAvailablePages().contains(MONTH)) {\n            selectedPage.set(MONTH);\n        } else if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n        }\n\n        setDate(LocalDate.of(year.getValue(), 1, 1).plusWeeks(weekOfYear));\n    }\n\n    /**\n     * Sends the request to the calendar view to display the given date and\n     * time. The view will switch to the {@link DayPage} and set the value of\n     * {@link #dateProperty()} to the date and {@link #requestedTimeProperty()}\n     * to the time.\n     *\n     * @param dateTime the date and time to show in the view\n     */\n    public final void showDateTime(LocalDateTime dateTime) {\n        requireNonNull(dateTime);\n\n        if (getAvailablePages().contains(DAY)) {\n            selectedPage.set(DAY);\n        } else if (getAvailablePages().contains(WEEK)) {\n            selectedPage.set(WEEK);\n        } else if (getAvailablePages().contains(MONTH)) {\n            selectedPage.set(MONTH);\n        } else if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n        }\n\n        setDate(dateTime.toLocalDate());\n        setRequestedTime(dateTime.toLocalTime());\n    }\n\n    /**\n     * Sends the request to the calendar view to display the given year and\n     * month. The view will switch to the {@link MonthPage} and set the value of\n     * {@link #dateProperty()} to the first day of the month.\n     *\n     * @param yearMonth the year and month to show in the view\n     */\n    public final void showYearMonth(YearMonth yearMonth) {\n        requireNonNull(yearMonth);\n\n        if (getAvailablePages().contains(MONTH)) {\n            selectedPage.set(MONTH);\n        } else if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n        }\n\n        setDate(yearMonth.atDay(1));\n    }\n\n    /**\n     * Sends the request to the calendar view to display the given year. The\n     * view will switch to the {@link YearPage} and set the value of\n     * {@link #dateProperty()} to the first day of the year.\n     *\n     * @param year the year to show in the view\n     */\n    public final void showYear(Year year) {\n        requireNonNull(year);\n        if (getAvailablePages().contains(YEAR)) {\n            selectedPage.set(YEAR);\n            setDate(year.atDay(1));\n        }\n    }\n\n    private final String CALENDAR_VIEW_CATEGORY = \"Calendar View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showSourceTrayProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowSourceTray((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowSourceTray();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Calendar Tray\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the calendar tray on the left\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showSearchResultsTrayProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowSearchResultsTray((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowSearchResultsTray();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Search Results Tray\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the search results tray on the right\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showSearchFieldProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowSearchField((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowSearchField();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Search Field\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the search field or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showSourceTrayButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowSourceTrayButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowSourceTrayButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Source Tray Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the source tray button or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showAddCalendarButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowAddCalendarButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowAddCalendarButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Add Calendar Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the button to add new calendars or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showPrintButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowPrintButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowPrintButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Print Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the button to print calendars or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showPageToolBarControlsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowPageToolBarControls((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowPageToolBarControls();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Page Controls\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the page-specific toolbar controls or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showPageSwitcherProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowPageSwitcher((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowPageSwitcher();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Page Switcher\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Visibility of the switcher.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showToolBarProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowToolBar((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowToolBar();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show ToolBar\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Visibility of the toolbar.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return CALENDAR_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/ContextMenuProvider.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.DateControl.ContextMenuParameter;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport com.calendarfx.view.DayViewBase.HoursLayoutStrategy;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.CustomMenuItem;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.control.Slider;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.util.Callback;\n\nimport java.text.MessageFormat;\nimport java.time.LocalTime;\nimport java.time.temporal.ChronoUnit;\n\nimport static com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy.HIDE;\nimport static com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy.SHOW;\nimport static com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy.SHOW_COMPRESSED;\nimport static com.calendarfx.view.VirtualGrid.OFF;\n\n/**\n * An implementation of the context menu callback required by\n * {@link DateControl}. Applications can subclass to create their own context\n * menus for the different views.\n *\n * @see DateControl#setContextMenuCallback(Callback)\n */\npublic class ContextMenuProvider implements Callback<ContextMenuParameter, ContextMenu> {\n\n    @Override\n    public ContextMenu call(ContextMenuParameter param) {\n        DateControl control = param.getDateControl();\n        ContextMenu contextMenu = null;\n\n        /*\n         * Check for WeekDayView first because it is a specialization of\n         * DayView. Otherwise we would always return the context menu of\n         * DayView.\n         */\n        if (control instanceof WeekDayView) {\n            contextMenu = getWeekDayViewMenu(param);\n        } else if (control instanceof DayView) {\n            contextMenu = getDayViewMenu(param);\n        } else if (control instanceof AllDayView) {\n            contextMenu = getAllDayViewMenu(param);\n        }\n\n        if (contextMenu == null || contextMenu.getItems().isEmpty()) {\n            return null;\n        }\n\n        return contextMenu;\n    }\n\n    /**\n     * Returns the context menu specific for a single {@link DayView}.\n     *\n     * @param param parameter object with the most relevant information for\n     *              creating a new context menu\n     * @return a context menu for a day view\n     */\n    protected ContextMenu getDayViewMenu(ContextMenuParameter param) {\n        return getDayViewBaseMenu(param);\n    }\n\n    /**\n     * Returns the context menu specific for a single {@link WeekDayView}. Week\n     * day views are used inside a {@link WeekView}.\n     *\n     * @param param parameter object with the most relevant information for\n     *              creating a new context menu\n     * @return a context menu for a week day view\n     */\n    protected ContextMenu getWeekDayViewMenu(ContextMenuParameter param) {\n        ContextMenu contextMenu = getDayViewBaseMenu(param);\n\n        WeekDayView weekDayView = (WeekDayView) param.getDateControl();\n        WeekView weekView = weekDayView.getWeekView();\n        Menu daysMenu = new Menu(Messages.getString(\"ContextMenuProvider.SHOW_DAYS\"));\n        int[] days = new int[]{5, 7, 14, 21, 28};\n        for (int d : days) {\n            String itemText = MessageFormat.format(Messages.getString(\"ContextMenuProvider.DAYS\"), d);\n            MenuItem item = new MenuItem(itemText);\n            item.setOnAction(evt -> weekView.setNumberOfDays(d));\n            daysMenu.getItems().add(item);\n        }\n\n        contextMenu.getItems().add(daysMenu);\n\n        return contextMenu;\n    }\n\n    private ContextMenu getDayViewBaseMenu(ContextMenuParameter param) {\n        ContextMenu contextMenu = new ContextMenu();\n\n        DateControl control = param.getDateControl();\n        if (control instanceof DayView) {\n            DayViewBase dayView = (DayViewBase) control;\n\n            MenuItem newEntry = new MenuItem(Messages.getString(\"ContextMenuProvider.ADD_NEW_EVENT\"));\n            newEntry.setOnAction(evt -> {\n                if (control.getLayout().equals(DateControl.Layout.SWIMLANE)) {\n                    Calendar calendar = control.getCalendarAt(param.getContextMenuEvent().getX(), param.getContextMenuEvent().getY()).orElse(null);\n                    control.createEntryAt(param.getZonedDateTime(), calendar);\n                } else {\n                    control.createEntryAt(param.getZonedDateTime());\n                }\n                contextMenu.hide();\n            });\n            contextMenu.getItems().add(newEntry);\n\n            /*\n             * Only add submenu if view does not use all 24 hours.\n             */\n            if (!(dayView.getStartTime().equals(LocalTime.MIN) && dayView.getEndTime().equals(LocalTime.MAX))) {\n                // Early / late hours menu\n                Menu earlyLateHoursMenu = new Menu(Messages.getString(\"ContextMenuProvider.EARLY_LATE_HOURS\"));\n                RadioMenuItem hideItem = new RadioMenuItem(Messages.getString(\"ContextMenuProvider.EARLY_LATE_HOURS_HIDE\"));\n                RadioMenuItem showItem = new RadioMenuItem(Messages.getString(\"ContextMenuProvider.EARLY_LATE_HOURS_SHOW\"));\n                RadioMenuItem showCompressedItem = new RadioMenuItem(Messages.getString(\"ContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED\"));\n                hideItem.setOnAction(evt -> dayView.setEarlyLateHoursStrategy(HIDE));\n                showItem.setOnAction(evt -> dayView.setEarlyLateHoursStrategy(SHOW));\n                showCompressedItem.setOnAction(evt -> dayView.setEarlyLateHoursStrategy(SHOW_COMPRESSED));\n                switch (dayView.getEarlyLateHoursStrategy()) {\n                    case HIDE:\n                        hideItem.setSelected(true);\n                        break;\n                    case SHOW:\n                        showItem.setSelected(true);\n                        break;\n                    case SHOW_COMPRESSED:\n                        showCompressedItem.setSelected(true);\n                        break;\n                    default:\n                        break;\n                }\n                ToggleGroup group = new ToggleGroup();\n                group.getToggles().setAll(hideItem, showItem, showCompressedItem);\n                earlyLateHoursMenu.getItems().setAll(hideItem, showItem, showCompressedItem);\n                contextMenu.getItems().add(earlyLateHoursMenu);\n            }\n\n            Menu gridMenu = new Menu(Messages.getString(\"ContextMenuProvider.GRID\"));\n            MenuItem gridOff = new MenuItem(Messages.getString(\"ContextMenuProvider.GRID_OFF\"));\n            gridOff.setOnAction(evt -> control.setVirtualGrid(OFF));\n            gridMenu.getItems().add(gridOff);\n            gridMenu.getItems().add(new SeparatorMenuItem());\n\n            int[] grids = new int[]{5, 10, 15, 30, 60};\n            for (int grid : grids) {\n                String itemText = MessageFormat.format(Messages.getString(\"ContextMenuProvider.MINUTES\"), grid);\n                String itemTextShort = MessageFormat.format(Messages.getString(\"ContextMenuProvider.MINUTES_SHORT\"), grid);\n                MenuItem gridItem = new MenuItem(itemText);\n                gridMenu.getItems().add(gridItem);\n                gridItem.setOnAction(evt -> control.setVirtualGrid(new VirtualGrid(itemText, itemTextShort, ChronoUnit.MINUTES, grid)));\n            }\n\n            contextMenu.getItems().add(gridMenu);\n\n            Menu hoursMenu = new Menu(Messages.getString(\"ContextMenuProvider.SHOW_HOURS\"));\n\n            Slider slider = new Slider(40, 200, 50);\n            slider.setPrefWidth(100);\n            slider.setValue(dayView.getHourHeight());\n            slider.valueProperty().addListener(it -> {\n                dayView.setHoursLayoutStrategy(HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n                dayView.setHourHeight(slider.getValue());\n            });\n\n            CustomMenuItem hourHeight = new CustomMenuItem(slider);\n            hourHeight.setHideOnClick(false);\n            hoursMenu.getItems().add(hourHeight);\n            hoursMenu.getItems().add(new SeparatorMenuItem());\n\n            int[] hours = new int[]{4, 6, 8, 10, 12, 18, 24};\n            for (int h : hours) {\n                String labelText = MessageFormat.format(Messages.getString(\"ContextMenuProvider.HOURS\"), h);\n                MenuItem item = new MenuItem(labelText);\n                item.setOnAction(evt -> {\n                    dayView.setEarlyLateHoursStrategy(EarlyLateHoursStrategy.SHOW);\n                    dayView.setHoursLayoutStrategy(HoursLayoutStrategy.FIXED_HOUR_COUNT);\n                    dayView.setVisibleHours(h);\n                });\n                hoursMenu.getItems().add(item);\n            }\n            contextMenu.getItems().add(hoursMenu);\n        }\n\n        return contextMenu;\n    }\n\n    /**\n     * Returns the context menu specific for an {@link AllDayView}.\n     *\n     * @param param parameter object with the most relevant information for\n     *              creating a new context menu\n     * @return a context menu for an all day view\n     */\n    protected ContextMenu getAllDayViewMenu(ContextMenuParameter param) {\n        ContextMenu contextMenu = new ContextMenu();\n\n        DateControl control = param.getDateControl();\n        if (control instanceof AllDayView) {\n            MenuItem newEntry = new MenuItem(Messages.getString(\"ContextMenuProvider.ADD_NEW_EVENT\"));\n            newEntry.setOnAction(evt -> {\n                control.createEntryAt(param.getZonedDateTime());\n                contextMenu.hide();\n            });\n            contextMenu.getItems().add(newEntry);\n        }\n\n        if (contextMenu.getItems().isEmpty()) {\n            return null;\n        }\n\n        return contextMenu;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/CreateAndDeleteHandler.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport javafx.application.Platform;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\n\nimport java.time.ZonedDateTime;\nimport java.util.Optional;\n\nclass CreateAndDeleteHandler extends DeleteHandler {\n\n\n    public CreateAndDeleteHandler(DateControl control) {\n        super(control);\n        dateControl.addEventHandler(MouseEvent.MOUSE_CLICKED, this::createEntry);\n    }\n\n    private void createEntry(MouseEvent evt) {\n        if (evt.getButton().equals(MouseButton.PRIMARY) && evt.getClickCount() == dateControl.getCreateEntryClickCount()) {\n\n            if (!evt.isStillSincePress()) {\n                return;\n            }\n\n            if (dateControl instanceof DayViewBase) {\n                DayViewBase dayViewBase = (DayViewBase) dateControl;\n                if (dayViewBase.isEditAvailability()) {\n                    LoggingDomain.VIEW.fine(\"no new entry created because day view is currently editing availability\");\n                    return;\n                }\n            }\n\n            LoggingDomain.VIEW.fine(\"create entry mouse event received inside control: \" + dateControl.getClass().getSimpleName());\n\n            ZonedDateTime time = ZonedDateTime.now().withZoneSameInstant(dateControl.getZoneId());\n            if (dateControl instanceof ZonedDateTimeProvider) {\n                ZonedDateTimeProvider provider = (ZonedDateTimeProvider) dateControl;\n                time = provider.getZonedDateTimeAt(evt.getX(), evt.getY(), dateControl.getZoneId());\n            }\n\n            if (dateControl.getCalendars().isEmpty()) {\n\n                Alert alert = new Alert(AlertType.WARNING);\n                alert.setTitle(Messages.getString(\"DateControl.TITLE_CALENDAR_PROBLEM\"));\n                alert.setHeaderText(Messages.getString(\"DateControl.HEADER_TEXT_NO_CALENDARS_DEFINED\"));\n                alert.setContentText(Messages.getString(\"DateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED\"));\n                alert.show();\n\n            } else {\n\n                Optional<Calendar> calendar = dateControl.getCalendarAt(evt.getX(), evt.getY());\n                if (time != null) {\n                    Entry<?> entry = dateControl.createEntryAt(time, calendar.orElse(null));\n                    if (dateControl.isShowDetailsUponEntryCreation()) {\n                        Platform.runLater(() -> dateControl.fireEvent(new RequestEvent(dateControl, dateControl, entry)));\n                    }\n                }\n            }\n\n            evt.consume();\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DateControl.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.util.ViewHelper;\nimport com.calendarfx.util.WeakList;\nimport com.calendarfx.view.page.DayPage;\nimport com.calendarfx.view.popover.DatePopOver;\nimport com.calendarfx.view.popover.EntryPopOverContentPane;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyListProperty;\nimport javafx.beans.property.ReadOnlyListWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.collections.ObservableSet;\nimport javafx.geometry.Point2D;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.input.ContextMenuEvent;\nimport javafx.scene.input.InputEvent;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.Paint;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Modality;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PopOver;\nimport org.controlsfx.control.PopOver.ArrowLocation;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.text.MessageFormat;\nimport java.time.DayOfWeek;\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.WeekFields;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static java.time.DayOfWeek.SATURDAY;\nimport static java.time.DayOfWeek.SUNDAY;\nimport static java.util.Objects.requireNonNull;\nimport static javafx.scene.input.ContextMenuEvent.CONTEXT_MENU_REQUESTED;\n\n/**\n * The superclass for all controls that are showing calendar information. This\n * class is responsible for:\n *\n * <ul>\n * <li>Binding to other date controls</li>\n * <li>Providing the current date, \"today\", first day of week</li>\n * <li>Creating sources, calendars, entries</li>\n * <li>Context menu</li>\n * <li>Showing details for a given date or entry</li>\n * <li>Providing a virtual grid for editing</li>\n * <li>Selection handling</li>\n * <li>Printing</li>\n * </ul>\n * <h2>Binding</h2> Date controls are bound to each other to create complex date\n * controls like the {@link CalendarView}. When date controls are bound to each\n * other via the {@link #bind(DateControl, boolean)} method then most of their\n * properties will be bound to each other. This not only includes date and time\n * zone properties but also all the factory and detail callbacks. This allows an\n * application to create a complex calendar control and to configure only that\n * control without worrying about the date controls that are nested inside\n * it. The children will all \"inherit\" their settings from the parent control.\n *\n * <h2>Current Date, Today, First Day of Week</h2> The {@link #dateProperty()}\n * is used to store the date that the control has to display. For the\n * {@link DayView} this would mean that it has to show exactly that date. The\n * {@link DetailedWeekView} would only have to guarantee that it shows the week that\n * contains this date. For this the {@link DetailedWeekView} uses the\n * {@link #getFirstDayOfWeek()} method that looks up its value from the week\n * fields stored in the {@link #weekFieldsProperty()}. The\n * {@link #todayProperty()} is mainly used for highlighting today's date in the\n * view (e.g. a red background).\n *\n * <h2>Creating Sources, Calendars, Entries</h2> The date control uses various\n * factories to create new sources, calendars, and entries. Each factory has to\n * implement the {@link Callback} interface. The factories will be invoked when\n * the application calls {@link #createCalendarSource()} or\n * {@link #createEntryAt(ZonedDateTime)}.\n *\n * <h2>Context Menu</h2> Date controls can either set a context menu explicitly\n * via {@link #setContextMenu(ContextMenu)} or by providing a callback that gets\n * invoked every time the context menu event is received (see\n * {@link #setContextMenuCallback(Callback)}). If a context menu has been set\n * explicitly then the callback will never be called again.\n *\n * <h2>Details for Entries and Dates</h2> When clicking on an entry or a date\n * the user wants to see details regarding the entry or the date. Callbacks for\n * this can be registered via {@link #setEntryDetailsCallback(Callback)} and\n * {@link #setDateDetailsCallback(Callback)}. The callbacks can decide which\n * kind of user interface they want to show to the user. The default\n * implementation for both callbacks is a {@link PopOver} control from the\n * <a href=\"http://controlsfx.org\">ControlsFX</a> project.\n *\n * <h2>Selection Handling</h2> Date controls use a very simple selection\n * concept. All selected entries are stored inside an observable list (see\n * {@link #getSelections()}). The controls support single and multiple\n * selections (see {@link #setSelectionMode(SelectionMode)}). Due to the binding\n * approach it does not matter in which child date control an entry gets\n * selected. All controls will always know which entries are selected.\n *\n * <h2>Virtual Grid</h2> A virtual grid is used for editing. It allows the start\n * and end times of entries to snap to \"virtual\" grid lines. The grid can be\n * used to make the times always snap to 5, 10, 15, 30 minutes for example. This\n * makes it easier to align entries to each other and covers the most common use\n * cases. More precise times can always be set in the details.\n */\npublic abstract class DateControl extends CalendarFXControl {\n\n    private Boolean usesOwnContextMenu;\n\n    private final InvalidationListener updateCalendarListListener = (Observable it) -> updateCalendarList();\n\n    private final WeakInvalidationListener weakUpdateCalendarListListener = new WeakInvalidationListener(updateCalendarListListener);\n\n    /**\n     * Constructs a new date control and initializes all factories and callbacks\n     * with default implementations.\n     */\n    protected DateControl() {\n        setOnMouseClicked(evt -> requestFocus());\n\n        setUsagePolicy(count -> {\n            if (count < 0) {\n                throw new IllegalArgumentException(\"usage count can not be smaller than zero, but was \" + count);\n            }\n\n            switch (count) {\n                case 0:\n                    return Usage.NONE;\n                case 1:\n                    return Usage.VERY_LOW;\n                case 2:\n                    return Usage.LOW;\n                case 3:\n                    return Usage.MEDIUM;\n                case 4:\n                    return Usage.HIGH;\n                case 5:\n                default:\n                    return Usage.VERY_HIGH;\n            }\n        });\n\n        getWeekendDays().add(SATURDAY);\n        getWeekendDays().add(SUNDAY);\n\n        /*\n         * Every date control is initially populated with a default source and\n         * calendar.\n         */\n        CalendarSource defaultCalendarSource = new CalendarSource(Messages.getString(\"DateControl.DEFAULT_CALENDAR_SOURCE_NAME\"));\n        Calendar defaultCalendar = new Calendar(Messages.getString(\"DateControl.DEFAULT_CALENDAR_NAME\"));\n        defaultCalendarSource.getCalendars().add(defaultCalendar);\n        getCalendarSources().add(defaultCalendarSource);\n\n        getCalendarSources().addListener(weakUpdateCalendarListListener);\n\n        /*\n         * The popover content callback creates a content node that will make\n         * out the content of the popover used to display entry details.\n         */\n        setEntryDetailsPopOverContentCallback(param -> new EntryPopOverContentPane(param.getPopOver(), param.getDateControl(), param.getEntry()));\n\n        /*\n         * The default calendar provider returns the first calendar which is visible and not read-only.\n         */\n        setDefaultCalendarProvider(control -> {\n            List<CalendarSource> sources = control.getCalendarSources();\n            for (CalendarSource s : sources) {\n                List<? extends Calendar> calendars = s.getCalendars();\n                if (calendars != null && !calendars.isEmpty()) {\n                    for (Calendar c : calendars) {\n                        if (!c.isReadOnly() && control.isCalendarVisible(c)) {\n                            return c;\n                        }\n                    }\n                }\n            }\n\n            return null;\n        });\n\n        setEntryFactory(param -> {\n            DateControl dateControl = param.getDateControl();\n\n            ZonedDateTime time = param.getZonedDateTime();\n            DayOfWeek firstDayOfWeek = dateControl.getFirstDayOfWeek();\n\n            VirtualGrid grid = dateControl.getVirtualGrid();\n            ZonedDateTime lowerTime = grid.adjustTime(time, false, firstDayOfWeek);\n            ZonedDateTime upperTime = grid.adjustTime(time, true, firstDayOfWeek);\n\n            if (Duration.between(time, lowerTime).abs().minus(Duration.between(time, upperTime).abs()).isNegative()) {\n                time = lowerTime;\n            } else {\n                time = upperTime;\n            }\n\n            Entry<Object> entry = new Entry<>(Messages.getString(\"DateControl.DEFAULT_ENTRY_TITLE\"));\n            Interval interval = new Interval(time.toLocalDateTime(), time.toLocalDateTime().plusHours(1), time.getZone());\n            entry.setInterval(interval);\n\n            if (dateControl instanceof AllDayView) {\n                entry.setFullDay(true);\n            }\n\n            return entry;\n        });\n\n        setEntryDetailsCallback(param -> {\n            InputEvent evt = param.getInputEvent();\n            if (evt instanceof MouseEvent) {\n                MouseEvent mouseEvent = (MouseEvent) evt;\n                if (mouseEvent.getClickCount() == 2) {\n                    param.getDateControl().showEntryDetails(param.getEntry(), param.getNode(), param.getOwner(), param.getScreenY());\n                    return true;\n                }\n            } else {\n                param.getDateControl().showEntryDetails(param.getEntry(), param.getNode(), param.getOwner(), param.getScreenY());\n                return true;\n            }\n\n            return false;\n        });\n\n        setDateDetailsCallback(param -> {\n            InputEvent evt = param.getInputEvent();\n            if (evt == null) {\n                param.getDateControl().showDateDetails(param.getOwner(), param.getLocalDate(), param.getScreenX(), param.getScreenY());\n                return true;\n            } else if (evt instanceof MouseEvent) {\n                MouseEvent mouseEvent = (MouseEvent) evt;\n                if (mouseEvent.getClickCount() == 1) {\n                    param.getDateControl().showDateDetails(param.getOwner(), param.getLocalDate(), param.getScreenX(), param.getScreenY());\n                    return true;\n                }\n            }\n\n            return false;\n        });\n\n        setContextMenuCallback(new ContextMenuProvider());\n\n        setEntryContextMenuCallback(param -> {\n            EntryViewBase<?> entryView = param.getEntryView();\n            Entry<?> entry = entryView.getEntry();\n\n            ContextMenu contextMenu = new ContextMenu();\n\n            /*\n             * Show dialog / popover with entry details.\n             */\n            MenuItem informationItem = new MenuItem(Messages.getString(\"DateControl.MENU_ITEM_INFORMATION\"));\n            informationItem.setOnAction(evt -> {\n                Callback<EntryDetailsParameter, Boolean> detailsCallback = param.getDateControl().getEntryDetailsCallback();\n                if (detailsCallback != null) {\n                    ContextMenuEvent ctxEvent = param.getContextMenuEvent();\n                    detailsCallback.call(new EntryDetailsParameter(ctxEvent, param.getDateControl(), entryView.getEntry(), entryView, entryView, ctxEvent.getScreenX(), ctxEvent.getScreenY()));\n                }\n            });\n            contextMenu.getItems().add(informationItem);\n\n            String stylesheet;\n\n            if (Boolean.getBoolean(\"atlantafx\")) {\n                stylesheet = requireNonNull(CalendarFXControl.class.getResource(\"atlantafx.css\")).toExternalForm();\n            } else {\n                stylesheet = requireNonNull(CalendarFXControl.class.getResource(\"calendar.css\")).toExternalForm();\n            }\n\n            /*\n             * Assign entry to different calendars.\n             */\n            Menu calendarMenu = new Menu(Messages.getString(\"DateControl.MENU_CALENDAR\"));\n            for (Calendar calendar : param.getDateControl().getCalendars()) {\n                RadioMenuItem calendarItem = new RadioMenuItem(calendar.getName());\n                calendarItem.setOnAction(evt -> entry.setCalendar(calendar));\n                calendarItem.setDisable(calendar.isReadOnly());\n                calendarItem.setSelected(calendar.equals(param.getCalendar()));\n                calendarMenu.getItems().add(calendarItem);\n\n                StackPane graphic = new StackPane();\n                graphic.getStylesheets().add(stylesheet);\n\n                /*\n                 * Icon has to be wrapped in a stackpane so that a stylesheet\n                 * can be added to it.\n                 */\n                Rectangle icon = new Rectangle(10, 10);\n                icon.setArcHeight(2);\n                icon.setArcWidth(2);\n                icon.getStyleClass().setAll(calendar.getStyle() + \"-icon\");\n                graphic.getChildren().add(icon);\n\n                calendarItem.setGraphic(graphic);\n            }\n\n            calendarMenu.setDisable(param.getCalendar().isReadOnly());\n            contextMenu.getItems().add(calendarMenu);\n\n            if (param.getDateControl().getEntryEditPolicy().call(new EntryEditParameter(param.getDateControl(), entry, EditOperation.DELETE))) {\n                /*\n                 * Delete calendar entry.\n                 */\n                MenuItem delete = new MenuItem(Messages.getString(\"DateControl.MENU_ITEM_DELETE\"));\n                contextMenu.getItems().add(delete);\n                delete.setDisable(param.getCalendar().isReadOnly());\n                delete.setOnAction(evt -> {\n                    Calendar calendar = entry.getCalendar();\n                    if (!calendar.isReadOnly()) {\n                        if (entry.isRecurrence()) {\n                            Entry<?> recurrenceSourceEntry = entry.getRecurrenceSourceEntry();\n                            if (recurrenceSourceEntry != null) {\n                                recurrenceSourceEntry.removeFromCalendar();\n                            }\n                        } else {\n                            entry.removeFromCalendar();\n                        }\n                    }\n                });\n            }\n\n            return contextMenu;\n        });\n\n        setCalendarSourceFactory(param -> {\n            CalendarSource source = new CalendarSource(Messages.getString(\"DateControl.DEFAULT_NEW_CALENDAR_SOURCE\"));\n            Calendar calendar = new Calendar(Messages.getString(\"DateControl.DEFAULT_NEW_CALENDAR\"));\n            calendar.setShortName(Messages.getString(\"DateControl.DEFAULT_NEW_CALENDAR\").substring(0, 1));\n            source.getCalendars().add(calendar);\n            return source;\n        });\n\n        addEventHandler(CONTEXT_MENU_REQUESTED, evt -> {\n\n            /*\n             * If a context menu was specified by calling setContextMenu() then\n             * we will not use the callback to produce one.\n             */\n            if (null == usesOwnContextMenu) {\n                usesOwnContextMenu = getContextMenu() != null;\n            }\n\n            if (!usesOwnContextMenu) {\n                Callback<ContextMenuParameter, ContextMenu> callback = getContextMenuCallback();\n                if (callback != null) {\n                    Callback<DateControl, Calendar> calendarProvider = getDefaultCalendarProvider();\n                    Calendar calendar = calendarProvider.call(DateControl.this);\n                    ZonedDateTime time = ZonedDateTime.now();\n                    if (DateControl.this instanceof ZonedDateTimeProvider) {\n                        ZonedDateTimeProvider provider = (ZonedDateTimeProvider) DateControl.this;\n                        time = provider.getZonedDateTimeAt(evt.getX(), evt.getY(), getZoneId());\n                    }\n                    ContextMenuParameter param = new ContextMenuParameter(evt, DateControl.this, calendar, time);\n                    ContextMenu menu = callback.call(param);\n                    if (menu != null) {\n                        menu.show(getScene().getWindow(), evt.getScreenX(), evt.getScreenY());\n                    }\n\n                    evt.consume();\n                }\n            }\n        });\n\n        getAvailableZoneIds().add(ZoneId.of(\"Europe/Zurich\"));\n        getAvailableZoneIds().add(ZoneId.of(\"Europe/Helsinki\"));\n        getAvailableZoneIds().add(ZoneId.of(\"Europe/London\"));\n        getAvailableZoneIds().add(ZoneId.of(\"US/Eastern\"));\n        getAvailableZoneIds().add(ZoneId.of(\"US/Central\"));\n        getAvailableZoneIds().add(ZoneId.of(\"US/Pacific\"));\n\n        createEntryClickCountProperty().addListener(it -> {\n            int createEntryClickCount = getCreateEntryClickCount();\n            if (createEntryClickCount <= 0 || createEntryClickCount > 3) {\n                throw new IllegalArgumentException(\"the click count for creating new entries must be between 1 and 3 but was \" + createEntryClickCount);\n            }\n        });\n    }\n\n    private final ObservableMap<Calendar, BooleanProperty> calendarVisibilityMap = FXCollections.observableHashMap();\n\n    public final ObservableMap<Calendar, BooleanProperty> getCalendarVisibilityMap() {\n        return calendarVisibilityMap;\n    }\n\n    public final BooleanProperty getCalendarVisibilityProperty(Calendar calendar) {\n        return calendarVisibilityMap.computeIfAbsent(calendar, cal -> new SimpleBooleanProperty(DateControl.this, \"visible\", true));\n    }\n\n    public final boolean isCalendarVisible(Calendar calendar) {\n        return getCalendarVisibilityProperty(calendar).get();\n    }\n\n    public final void setCalendarVisibility(Calendar calendar, boolean visible) {\n        getCalendarVisibilityProperty(calendar).set(visible);\n    }\n\n    public enum Layer {\n\n        /**\n         * Base (and default) presentation layer for entry views.\n         */\n        BASE,\n\n        /**\n         * Top presentation layer for entry views. Presented view entries will be visible above base layer.\n         * Uses simple layout system, which does not support resolving of overlapping entries.\n         */\n        TOP\n    }\n\n    private final ObservableSet<Layer> visibleLayers = FXCollections.observableSet(Layer.BASE, Layer.TOP);\n\n    /**\n     * Collection of layers which should be visible/presented.\n     */\n    public final ObservableSet<Layer> visibleLayersProperty() {\n        return visibleLayers;\n    }\n\n    /**\n     * Requests that the date control should reload its data and recreate its\n     * entry views. Normally applications do not have to call this method. It is\n     * more like a backdoor for client / server applications where the server is\n     * unable to push changes to the client. In this case the client must\n     * frequently trigger an explicit refresh.\n     */\n    public final void refreshData() {\n        getProperties().put(\"refresh.data\", true);\n\n        getBoundDateControls().forEach(DateControl::refreshData);\n    }\n\n\n    /**\n     * Creates a new calendar source that will be added to the list of calendar\n     * sources of this date control. The method delegates the actual creation of\n     * the calendar source to a factory, which can be specified by calling\n     * {@link #setCalendarSourceFactory(Callback)}.\n     *\n     * @see #setCalendarSourceFactory(Callback)\n     */\n    public final void createCalendarSource() {\n        Callback<CreateCalendarSourceParameter, CalendarSource> factory = getCalendarSourceFactory();\n        if (factory != null) {\n            CreateCalendarSourceParameter param = new CreateCalendarSourceParameter(this);\n            CalendarSource calendarSource = factory.call(param);\n            if (calendarSource != null && !getCalendarSources().contains(calendarSource)) {\n                getCalendarSources().add(calendarSource);\n            }\n        }\n    }\n\n    // dragged entry support\n    private final ObjectProperty<DraggedEntry> draggedEntry = new SimpleObjectProperty<>(this, \"draggedEntry\");\n\n    /**\n     * Stores a {@link DraggedEntry} instance, which serves as a wrapper around\n     * the actual entry that is currently being edited by the user. The\n     * framework creates this wrapper when the user starts a drag and adds it to\n     * the date control. This allows the framework to show the entry at its old\n     * and new location at the same time. It also ensures that the calendar does\n     * not fire any events before the user has committed the entry to a new\n     * location.\n     *\n     * @return the dragged entry\n     */\n    public final ObjectProperty<DraggedEntry> draggedEntryProperty() {\n        return draggedEntry;\n    }\n\n    /**\n     * Returns the value of {@link #draggedEntryProperty()}.\n     *\n     * @return the dragged entry\n     */\n    public final DraggedEntry getDraggedEntry() {\n        return draggedEntry.get();\n    }\n\n    /**\n     * Sets the value of {@link #draggedEntryProperty()}.\n     *\n     * @param entry the dragged entry\n     */\n    public final void setDraggedEntry(DraggedEntry entry) {\n        draggedEntryProperty().set(entry);\n    }\n\n    /**\n     * Creates a new entry at the given time. The method delegates the actual\n     * instance creation to the entry factory (see\n     * {@link #entryFactoryProperty()}). The factory receives a parameter object\n     * that contains the default calendar where the entry can be added, however\n     * the factory can choose to add the entry to any calendar it likes. Please\n     * note that the time passed to the factory will be adjusted based on the\n     * current virtual grid settings (see {@link #virtualGridProperty()}).\n     *\n     * @param time the time point where the entry will be created (the entry start\n     *             time)\n     * @return the new calendar entry or null if no entry could be created\n     * @see #setEntryFactory(Callback)\n     * @see #setVirtualGrid(VirtualGrid)\n     */\n    public final Entry<?> createEntryAt(ZonedDateTime time) {\n        return createEntryAt(time, null, false);\n    }\n\n    /**\n     * Creates a new entry at the given time. The method delegates the actual\n     * instance creation to the entry factory (see\n     * {@link #entryFactoryProperty()}). The factory receives a parameter object\n     * that contains the calendar where the entry can be added, however the\n     * factory can choose to add the entry to any calendar it likes. Please note\n     * that the time passed to the factory will be adjusted based on the current\n     * virtual grid settings (see {@link #virtualGridProperty()}).\n     *\n     * @param time     the time point where the entry will be created (the entry start\n     *                 time)\n     * @param calendar the calendar to which the new entry will be added (if null the\n     *                 default calendar provider will be invoked)\n     * @return the new calendar entry or null if no entry could be created\n     * @see #setEntryFactory(Callback)\n     * @see #setVirtualGrid(VirtualGrid)\n     */\n    public final Entry<?> createEntryAt(ZonedDateTime time, Calendar calendar) {\n        return createEntryAt(time, calendar, false);\n    }\n\n    /**\n     * Creates a new entry at the given time. The method delegates the actual\n     * instance creation to the entry factory (see\n     * {@link #entryFactoryProperty()}). The factory receives a parameter object\n     * that contains the calendar where the entry can be added, however the\n     * factory can choose to add the entry to any calendar it likes. Please note\n     * that the time passed to the factory will be adjusted based on the current\n     * virtual grid settings (see {@link #virtualGridProperty()}).\n     *\n     * @param time            the time point where the entry will be created (the entry start\n     *                        time)\n     * @param calendar        the calendar to which the new entry will be added (if null the\n     *                        default calendar provider will be invoked)\n     * @param initiallyHidden entry will be invisible until the application calls {@link Entry#setHidden(boolean)}.\n     * @return the new calendar entry or null if no entry could be created\n     * @see #setEntryFactory(Callback)\n     * @see #setVirtualGrid(VirtualGrid)\n     */\n    public final Entry<?> createEntryAt(ZonedDateTime time, Calendar calendar, boolean initiallyHidden) {\n        requireNonNull(time);\n        VirtualGrid grid = getVirtualGrid();\n        if (grid != null) {\n            ZonedDateTime timeA = grid.adjustTime(time, false, getFirstDayOfWeek());\n            ZonedDateTime timeB = grid.adjustTime(time, true, getFirstDayOfWeek());\n            if (Duration.between(time, timeA).abs().minus(Duration.between(time, timeB).abs()).isNegative()) {\n                time = timeA;\n            } else {\n                time = timeB;\n            }\n        }\n\n        if (calendar == null) {\n            Callback<DateControl, Calendar> defaultCalendarProvider = getDefaultCalendarProvider();\n            calendar = defaultCalendarProvider.call(this);\n        }\n\n        if (calendar != null) {\n            if (calendar.isReadOnly()) {\n                return null;\n            }\n\n            /*\n             * We have to ensure that the calendar is visible, otherwise the new\n             * entry would not be shown to the user.\n             */\n            setCalendarVisibility(calendar, true);\n\n            CreateEntryParameter param = new CreateEntryParameter(this, calendar, time);\n            Callback<CreateEntryParameter, Entry<?>> factory = getEntryFactory();\n            Entry<?> entry = factory.call(param);\n\n            /*\n             * This is OK. The factory can return NULL. In this case we\n             * assume that the application does not allow to create an entry\n             * at the given location.\n             */\n            if (entry != null) {\n                entry.setHidden(initiallyHidden);\n                entry.setCalendar(calendar);\n            }\n\n            return entry;\n\n        } else {\n\n            Alert alert = new Alert(AlertType.WARNING);\n            alert.initOwner(this.getScene().getWindow());\n            alert.initModality(Modality.WINDOW_MODAL);\n            alert.setTitle(Messages.getString(\"DateControl.TITLE_CALENDAR_PROBLEM\"));\n            alert.setHeaderText(Messages.getString(\"DateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY\"));\n            String newLine = System.getProperty(\"line.separator\");\n            alert.setContentText(MessageFormat.format(Messages.getString(\"DateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY\"), newLine));\n            alert.show();\n\n        }\n\n        return null;\n    }\n\n    /**\n     * Returns the calendar shown at the given location. This method returns an\n     * optional value. Calling this method might or might not make sense,\n     * depending on the type of control and the current layout (see\n     * {@link #layoutProperty()}).\n     *\n     * @param x the x-coordinate to check\n     * @param y the y-coordinate to check\n     * @return the calendar at the given location\n     */\n    public Optional<Calendar> getCalendarAt(double x, double y) {\n        return Optional.empty();\n    }\n\n    /**\n     * Adjusts the current view / page in such a way that the given entry\n     * becomes visible.\n     *\n     * @param entry the entry to show\n     */\n    public final void showEntry(Entry<?> entry) {\n        requireNonNull(entry);\n        doShowEntry(entry, false, true);\n    }\n\n    /**\n     * Adjusts the current view / page in such a way that the given entry\n     * becomes visible and brings up the detail editor / UI for the entry\n     * (default is a popover).\n     *\n     * @param entry the entry to show\n     */\n    public final void editEntry(Entry<?> entry) {\n        requireNonNull(entry);\n        doShowEntry(entry, true, true);\n    }\n\n    /**\n     * Adjusts the current view / page in such a way that the given entry\n     * becomes visible and brings up the detail editor / UI for the entry\n     * (default is a popover).\n     *\n     * @param entry      the entry to show\n     * @param changeDate change the date of the control to the entry's start date\n     */\n    public final void editEntry(Entry<?> entry, boolean changeDate) {\n        requireNonNull(entry);\n        doShowEntry(entry, true, changeDate);\n    }\n\n    private void doShowEntry(Entry<?> entry, boolean startEditing, boolean changeDate) {\n        layout(); // important so that entry view bounds can be found\n\n        if (changeDate) {\n            setDate(entry.getStartDate());\n        }\n\n        Platform.runLater(() -> {\n            // do not scroll time when a location is already given\n            // a location is usually given when the user created a new entry via dragging\n            if (!entry.isFullDay()) {\n                // wiggle the requested time property\n                setRequestedTime(null);\n                setRequestedTime(entry.getStartTime());\n            }\n\n            Platform.runLater(() -> {\n                if (startEditing) {\n\n                    /*\n                     * The UI first needs to update itself so that the matching entry\n                     * view can be found.\n                     */\n                    Platform.runLater(() -> doEditEntry(entry));\n                } else {\n                    Platform.runLater(() -> doBounceEntry(entry));\n                }\n            });\n        });\n    }\n\n    private void doEditEntry(Entry<?> entry) {\n        EntryViewBase<?> entryView = findEntryView(entry);\n\n        Platform.runLater(() -> {\n            if (entryView != null) {\n                entryView.bounce();\n\n                Point2D location = entryView.localToScreen(0, 0);\n\n                Callback<EntryDetailsParameter, Boolean> callback = getEntryDetailsCallback();\n                EntryDetailsParameter param = new EntryDetailsParameter(null, this, entry, entryView, entryView, location.getX(), location.getY());\n                callback.call(param);\n            }\n        });\n    }\n\n    private void doBounceEntry(Entry<?> entry) {\n        EntryViewBase<?> entryView = findEntryView(entry);\n\n        if (entryView != null) {\n            entryView.bounce();\n        }\n    }\n\n    private PopOver entryPopOver;\n\n    private void showEntryDetails(Entry<?> entry, Node node, Node owner, double screenY) {\n        Callback<EntryDetailsPopOverContentParameter, Node> contentCallback = getEntryDetailsPopOverContentCallback();\n        if (contentCallback == null) {\n            throw new IllegalStateException(\"No content callback found for entry popover\");\n        }\n\n        if (entryPopOver == null || entryPopOver.isDetached()) {\n            entryPopOver = new PopOver();\n            entryPopOver.setAnimated(false); // important, otherwise too many side effects\n            entryPopOver.getStyleClass().add(\"entry-popover\");\n\n            String stylesheet;\n\n            if (Boolean.getBoolean(\"atlantafx\")) {\n                stylesheet = requireNonNull(CalendarFXControl.class.getResource(\"atlantafx.css\")).toExternalForm();\n            } else {\n                stylesheet = requireNonNull(CalendarFXControl.class.getResource(\"calendar.css\")).toExternalForm();\n            }\n\n            entryPopOver.skinProperty().addListener((o, old, nw) -> {\n                if (nw != null) {\n                  ((Region)nw.getNode()).getStylesheets().add(stylesheet);\n                }\n            });\n        }\n\n        EntryDetailsPopOverContentParameter param = new EntryDetailsPopOverContentParameter(entryPopOver, this, owner, entry);\n        Node content = contentCallback.call(param);\n\n        if (content == null) {\n            content = new Label(Messages.getString(\"DateControl.NO_CONTENT\"));\n        }\n\n        entryPopOver.setContentNode(content);\n\n        ArrowLocation location = ViewHelper.findPopOverArrowLocation(node);\n        if (location == null) {\n            location = ArrowLocation.TOP_LEFT;\n        }\n\n        entryPopOver.setArrowLocation(location);\n\n        Point2D position = ViewHelper.findPopOverArrowPosition(node, screenY, entryPopOver.getArrowSize(), location);\n\n        entryPopOver.show(owner, position.getX(), position.getY());\n    }\n\n    private PopOver datePopOver;\n\n    /**\n     * Creates a new {@link DatePopOver} and shows it attached to the given\n     * owner node.\n     *\n     * @param owner the owner node\n     * @param date  the date for which to display more detail\n     */\n    public void showDateDetails(Node owner, LocalDate date, double screenX, double screenY) {\n        if (datePopOver != null && datePopOver.isShowing()) {\n            datePopOver.hide();\n        }\n        datePopOver = new DatePopOver(this, date);\n        datePopOver.show(owner, screenX, screenY);\n    }\n\n    private abstract static class ContextMenuParameterBase {\n\n        private final DateControl dateControl;\n        private final ContextMenuEvent contextMenuEvent;\n\n        public ContextMenuParameterBase(ContextMenuEvent contextMenuEvent, DateControl dateControl) {\n            this.contextMenuEvent = requireNonNull(contextMenuEvent);\n            this.dateControl = requireNonNull(dateControl);\n        }\n\n        public ContextMenuEvent getContextMenuEvent() {\n            return contextMenuEvent;\n        }\n\n        public DateControl getDateControl() {\n            return dateControl;\n        }\n    }\n\n    /**\n     * The parameter object passed to the entry factory. It contains the most\n     * important parameters for creating a new entry: the requesting date\n     * control, the time point where the user performed a double click and the default\n     * calendar.\n     *\n     * @see DateControl#entryFactoryProperty()\n     * @see DateControl#defaultCalendarProviderProperty()\n     * @see DateControl#createEntryAt(ZonedDateTime)\n     */\n    public static final class CreateEntryParameter {\n\n        private final Calendar calendar;\n        private final ZonedDateTime zonedDateTime;\n        private final DateControl control;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param control  the control where the user / the application wants to\n         *                 create a new entry\n         * @param calendar the default calendar\n         * @param time     the time selected by the user in the date control\n         */\n        public CreateEntryParameter(DateControl control, Calendar calendar, ZonedDateTime time) {\n            this.control = requireNonNull(control);\n            this.calendar = requireNonNull(calendar);\n            this.zonedDateTime = requireNonNull(time);\n        }\n\n        /**\n         * Returns the calendar to which the entry will be added. Applications can add the new entry to\n         * this calendar by calling {@link Entry#setCalendar(Calendar)} or they can choose any other calendar.\n         *\n         * @return the calendar\n         */\n        public Calendar getCalendar() {\n            return calendar;\n        }\n\n        /**\n         * The time selected by the user.\n         *\n         * @return the start time for the new entry\n         */\n        public ZonedDateTime getZonedDateTime() {\n            return zonedDateTime;\n        }\n\n        /**\n         * The date control where the user performed the double click.\n         *\n         * @return the date control where the event happened\n         */\n        public DateControl getDateControl() {\n            return control;\n        }\n\n        @Override\n        public String toString() {\n            return \"CreateEntryParameter [calendar=\" + calendar\n                    + \", zonedDateTime=\" + zonedDateTime + \"]\";\n        }\n    }\n\n    private final ObjectProperty<Callback<CreateEntryParameter, Entry<?>>> entryFactory = new SimpleObjectProperty<>(this, \"entryFactory\");\n\n    /**\n     * A factory for creating new entries when the user double clicks inside the\n     * date control or when the application calls\n     * {@link #createEntryAt(ZonedDateTime)}. The factory can return NULL to\n     * indicate that no entry can be created at the given location.\n     *\n     * <h2>Code Example</h2>\n     * <p>\n     * The code below shows the default entry factory that is set on every date\n     * control.\n     * <pre>\n     * setEntryFactory(param -&gt; {\n     * \tDateControl control = param.getControl();\n     * \tVirtualGrid grid = control.getVirtualGrid();\n     * \tZonedDateTime time = param.getZonedDateTime();\n     * \tDayOfWeek firstDayOfWeek = getFirstDayOfWeek();\n     *\n     * \tZonedDateTime lowerTime = grid.adjustTime(time, false, firstDayOfWeek);\n     * \tZonedDateTime upperTime = grid.adjustTime(time, true, firstDayOfWeek);\n     *\n     * \tif (Duration.between(time, lowerTime).abs().minus(Duration.between(time, upperTime).abs()).isNegative()) {\n     * \t\ttime = lowerTime;\n     *    } else {\n     * \t\ttime = upperTime;\n     *    }\n     *\n     * \tEntry&lt;Object&gt; entry = new Entry&lt;&gt;(&quot;New Entry&quot;);\n     * \tentry.changeStartDate(time.toLocalDate());\n     * \tentry.changeStartTime(time.toLocalTime());\n     * \tentry.changeEndDate(entry.getStartDate());\n     * \tentry.changeEndTime(entry.getStartTime().plusHours(1));\n     *\n     * \tif (control instanceof AllDayView) {\n     * \t\tentry.setFullDay(true);\n     *    }\n     *\n     * \treturn entry;\n     * });\n     * </pre>\n     *\n     * @return the entry factory callback\n     */\n    public final ObjectProperty<Callback<CreateEntryParameter, Entry<?>>> entryFactoryProperty() {\n        return entryFactory;\n    }\n\n    /**\n     * Returns the value of {@link #entryFactoryProperty()}.\n     *\n     * @return the factory used for creating a new entry\n     */\n    public final Callback<CreateEntryParameter, Entry<?>> getEntryFactory() {\n        return entryFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #entryFactoryProperty()}.\n     *\n     * @param factory the factory used for creating a new entry\n     */\n    public final void setEntryFactory(Callback<CreateEntryParameter, Entry<?>> factory) {\n        Objects.requireNonNull(factory);\n        entryFactoryProperty().set(factory);\n    }\n\n    /*\n     * Calendar source callback.\n     */\n\n    /**\n     * The parameter object passed to the calendar source factory.\n     *\n     * @see DateControl#setCalendarSourceFactory(Callback)\n     */\n    public static final class CreateCalendarSourceParameter {\n\n        private final DateControl dateControl;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param dateControl the control where the source will be added\n         */\n        public CreateCalendarSourceParameter(DateControl dateControl) {\n            this.dateControl = requireNonNull(dateControl);\n        }\n\n        /**\n         * The control where the source will be added.\n         *\n         * @return the date control\n         */\n        public DateControl getDateControl() {\n            return dateControl;\n        }\n    }\n\n    private final ObjectProperty<Callback<CreateCalendarSourceParameter, CalendarSource>> calendarSourceFactory = new SimpleObjectProperty<>(this, \"calendarSourceFactory\");\n\n    /**\n     * A factory for creating a new calendar source, e.g. a new Google calendar\n     * account.\n     *\n     * <h2>Code Example</h2> The code below shows the default implementation of\n     * this factory. Applications can choose to bring up a full-featured user\n     * interface / dialog to specify the exact location of the source (either\n     * locally or over a network). A local calendar source might read its data\n     * from an XML file while a remote source could load data from a web\n     * service.\n     *\n     * <pre>\n     * setCalendarSourceFactory(param -&gt; {\n     * \tCalendarSource source = new CalendarSource(&quot;Calendar Source&quot;);\n     * \tCalendar calendar = new Calendar(&quot;Calendar&quot;);\n     * \tsource.getCalendars().add(calendar);\n     * \treturn source;\n     * });\n     * </pre>\n     * <p>\n     * The factory can be invoked by calling {@link #createCalendarSource()}.\n     *\n     * @return the calendar source factory\n     * @see #createCalendarSource()\n     */\n    public final ObjectProperty<Callback<CreateCalendarSourceParameter, CalendarSource>> calendarSourceFactoryProperty() {\n        return calendarSourceFactory;\n    }\n\n    /**\n     * Returns the value of {@link #calendarSourceFactoryProperty()}.\n     *\n     * @return the calendar source factory\n     */\n    public final Callback<CreateCalendarSourceParameter, CalendarSource> getCalendarSourceFactory() {\n        return calendarSourceFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #calendarSourceFactoryProperty()}.\n     *\n     * @param callback the callback used for creating a new calendar source\n     */\n    public final void setCalendarSourceFactory(Callback<CreateCalendarSourceParameter, CalendarSource> callback) {\n        calendarSourceFactoryProperty().set(callback);\n    }\n\n    /*\n     * Context menu callback for entries.\n     */\n\n    /**\n     * The parameter object passed to the context menu callback for entries.\n     *\n     * @see DateControl#entryContextMenuCallbackProperty()\n     */\n    public static final class EntryContextMenuParameter extends ContextMenuParameterBase {\n\n        private final EntryViewBase<?> entryView;\n\n        /**\n         * Constructs a new context menu parameter object.\n         *\n         * @param evt       the event that triggered the context menu\n         * @param control   the date control where the event occurred\n         * @param entryView the entry view for which the context menu will be created\n         */\n        public EntryContextMenuParameter(ContextMenuEvent evt, DateControl control, EntryViewBase<?> entryView) {\n            super(evt, control);\n            this.entryView = requireNonNull(entryView);\n        }\n\n        /**\n         * The entry view for which the context menu will be shown.\n         *\n         * @return the entry view\n         */\n        public EntryViewBase<?> getEntryView() {\n            return entryView;\n        }\n\n        /**\n         * Convenience method to easily look up the entry for which the view was\n         * created.\n         *\n         * @return the calendar entry\n         */\n        public Entry<?> getEntry() {\n            return entryView.getEntry();\n        }\n\n        /**\n         * Convenience method to easily look up the calendar of the entry for\n         * which the view was created.\n         *\n         * @return the calendar\n         */\n        public Calendar getCalendar() {\n            return getEntry().getCalendar();\n        }\n\n        @Override\n        public String toString() {\n            return \"EntryContextMenuParameter [entry=\" + entryView\n                    + \", dateControl =\" + getDateControl() + \"]\";\n        }\n    }\n\n    // entry edit support\n\n    /**\n     * Possible edit operations on an entry. This enum will be used as parameter of the\n     * callback set with {@link DateControl#setEntryEditPolicy}.\n     *\n     * @see #setEntryEditPolicy(Callback)\n     */\n    public enum EditOperation {\n\n        /**\n         * Checked if the start of an entry can be changed.\n         */\n        CHANGE_START,\n\n        /**\n         * Checked if the end of an entry can be changed.\n         */\n        CHANGE_END,\n\n        /**\n         * Checked if entry can be moved around, hence changing start and end time at\n         * the same time.\n         */\n        MOVE,\n\n        /**\n         * Checked if an entry can be deleted.\n         */\n        DELETE\n    }\n\n    /**\n     * Class used for parameter of {@link DateControl#entryEditPolicy}\n     * functional interface.\n     */\n    public static final class EntryEditParameter {\n\n        /**\n         * The date control the entity is associated with.\n         */\n        private final DateControl dateControl;\n\n        /**\n         * The entity the operation is operated on.\n         */\n        private final Entry<?> entry;\n\n        /**\n         * The operation.\n         */\n        private final DateControl.EditOperation editOperation;\n\n        public EntryEditParameter(DateControl dateControl, Entry<?> entry, EditOperation editOperation) {\n            this.dateControl = Objects.requireNonNull(dateControl);\n            this.entry = Objects.requireNonNull(entry);\n            this.editOperation = Objects.requireNonNull(editOperation);\n        }\n\n        /**\n         * The {@link DateControl} which is asking for a specific {@link DateControl.EditOperation} permission.\n         *\n         * @return The date control.\n         */\n        public DateControl getDateControl() {\n            return dateControl;\n        }\n\n        /**\n         * The entry where the {@link com.calendarfx.view.DateControl.EditOperation} should be applied.\n         *\n         * @return The entry.\n         */\n        public Entry<?> getEntry() {\n            return entry;\n        }\n\n        /**\n         * The actual edit operation.\n         *\n         * @return The edit operation.\n         */\n        public EditOperation getEditOperation() {\n            return editOperation;\n        }\n\n        @Override\n        public String toString() {\n            return \"EntryEditParameter{\" +\n                    \"dateControl=\" + dateControl +\n                    \", entry=\" + entry +\n                    \", editOperation=\" + editOperation +\n                    '}';\n        }\n    }\n\n    private final ObjectProperty<Callback<EntryEditParameter, Boolean>> entryEditPolicy = new SimpleObjectProperty<>(action -> true);\n\n    /**\n     * A property that stores a callback used for editing entries. If an edit operation will be executed\n     * on an entry then the callback will be invoked to determine if the operation is allowed. By default,\n     * all operations listed inside {@link EditOperation} are allowed.\n     *\n     * @return the property\n     * @see EditOperation\n     */\n    public final ObjectProperty<Callback<EntryEditParameter, Boolean>> entryEditPolicyProperty() {\n        return entryEditPolicy;\n    }\n\n    /**\n     * Returns the value of {@link #entryEditPolicy}.\n     *\n     * @return The entry edit policy callback\n     * @see EditOperation\n     */\n    public final Callback<EntryEditParameter, Boolean> getEntryEditPolicy() {\n        return entryEditPolicy.get();\n    }\n\n    /**\n     * Sets the value of {@link #entryEditPolicy}.\n     *\n     * @param policy the entry edit policy callback\n     * @see EditOperation\n     */\n    public final void setEntryEditPolicy(Callback<EntryEditParameter, Boolean> policy) {\n        Objects.requireNonNull(policy, \"The edit entry policy can not be null\");\n        this.entryEditPolicy.set(policy);\n    }\n\n    private final ObjectProperty<Callback<EntryContextMenuParameter, ContextMenu>> entryContextMenuCallback = new SimpleObjectProperty<>(this, \"entryFactory\");\n\n    /**\n     * A callback used for dynamically creating a context menu for a given\n     * entry view.\n     *\n     * <h2>Code Example</h2> The code below shows the default implementation of\n     * this callback.\n     * <pre>\n     * setEntryContextMenuCallback(param -&gt; {\n     * \tEntryViewBase&lt;?&gt; entryView = param.getEntryView();\n     * \tEntry&lt;?&gt; entry = entryView.getEntry();\n     *\n     * \tContextMenu contextMenu = new ContextMenu();\n     *\n     * \tMenuItem informationItem = new MenuItem(&quot;Information&quot;);\n     * \tinformationItem.setOnAction(evt -&gt; {\n     * \t\tCallback&lt;EntryDetailsParameter, Boolean&gt; detailsCallback = getEntryDetailsCallback();\n     * \t\tif (detailsCallback != null) {\n     * \t\t\tContextMenuEvent ctxEvent = param.getContextMenuEvent();\n     * \t\t\tEntryDetailsParameter entryDetailsParam = new EntryDetailsParameter(ctxEvent, DateControl.this, entryView, this, ctxEvent.getScreenX(), ctxEvent.getScreenY());\n     * \t\t\tdetailsCallback.call(entryDetailsParam);\n     *        }\n     *    });\n     * \tcontextMenu.getItems().add(informationItem);\n     *\n     * \tMenu calendarMenu = new Menu(&quot;Calendar&quot;);\n     * \tfor (Calendar calendar : getCalendars()) {\n     * \t\tMenuItem calendarItem = new MenuItem(calendar.getName());\n     * \t\tcalendarItem.setOnAction(evt -&gt; entry.setCalendar(calendar));\n     * \t\tcalendarMenu.getItems().add(calendarItem);\n     *    }\n     * \tcontextMenu.getItems().add(calendarMenu);\n     *\n     * \treturn contextMenu;\n     * });\n     * </pre>\n     *\n     * @return the property used for storing the callback\n     */\n    public final ObjectProperty<Callback<EntryContextMenuParameter, ContextMenu>> entryContextMenuCallbackProperty() {\n        return entryContextMenuCallback;\n    }\n\n    /**\n     * Returns the value of {@link #entryContextMenuCallbackProperty()}.\n     *\n     * @return the callback for creating a context menu for a given calendar\n     * entry\n     */\n    public final Callback<EntryContextMenuParameter, ContextMenu> getEntryContextMenuCallback() {\n        return entryContextMenuCallbackProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #entryContextMenuCallbackProperty()}.\n     *\n     * @param callback the callback used for creating a context menu for a calendar\n     *                 entry\n     */\n    public final void setEntryContextMenuCallback(Callback<EntryContextMenuParameter, ContextMenu> callback) {\n        entryContextMenuCallbackProperty().set(callback);\n    }\n\n    /*\n     * Context menu callback.\n     */\n\n    /**\n     * The parameter object passed to the context menu callback.\n     *\n     * @see DateControl#contextMenuCallbackProperty()\n     */\n    public static final class ContextMenuParameter extends ContextMenuParameterBase {\n\n        private final Calendar calendar;\n        private final ZonedDateTime zonedDateTime;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param evt         the event that triggered the context menu\n         * @param dateControl the date control where the event occurred\n         * @param calendar    the (default) calendar where newly created entries should\n         *                    be added (can be null if no editable calendar was found)\n         * @param time        the time point where the mouse click occurred\n         */\n        public ContextMenuParameter(ContextMenuEvent evt, DateControl dateControl, Calendar calendar, ZonedDateTime time) {\n            super(evt, dateControl);\n            this.calendar = calendar;\n            this.zonedDateTime = time;\n        }\n\n        /**\n         * The (default) calendar where newly created entries should be added.\n         * Only relevant if the context menu is actually used for creating new\n         * entries. This can be different from application to application.\n         *\n         * @return the (default) calendar for adding new entries\n         */\n        public Calendar getCalendar() {\n            return calendar;\n        }\n\n        /**\n         * The time point where the mouse click occurred.\n         *\n         * @return the time shown at the mouse click location\n         */\n        public ZonedDateTime getZonedDateTime() {\n            return zonedDateTime;\n        }\n\n        @Override\n        public String toString() {\n            return \"ContextMenuParameter [calendar=\" + calendar + \", zonedDateTime=\" + zonedDateTime + \"]\";\n        }\n    }\n\n    private final ObjectProperty<Callback<ContextMenuParameter, ContextMenu>> contextMenuCallback = new SimpleObjectProperty<>(this, \"contextMenuCallback\");\n\n    /**\n     * The context menu callback that will be invoked when the user triggers the\n     * context menu by clicking in an area without an entry view. Using a\n     * callback allows the application to create context menus with different\n     * content, depending on the current state of the application and the\n     * location of the click.\n     *\n     * <h2>Code Example</h2>\n     * <p>\n     * The code below shows a part of the default implementation:\n     * <pre>\n     * setContextMenuCallback(param -&gt; {\n     * \tContextMenu menu = new ContextMenu();\n     * \tMenuItem newEntryItem = new MenuItem(&quot;Add New Event&quot;);\n     * \tnewEntryItem.setOnAction(evt -&gt; {\n     * \t\tcreateEntryAt(param.getZonedDateTime());\n     *    });\n     * \tmenu.getItems().add(newEntry);\n     * \treturn menu;\n     * });\n     * </pre>\n     *\n     * @return the context menu callback\n     */\n    public final ObjectProperty<Callback<ContextMenuParameter, ContextMenu>> contextMenuCallbackProperty() {\n        return contextMenuCallback;\n    }\n\n    /**\n     * Returns the value of {@link #contextMenuCallbackProperty()}.\n     *\n     * @return the context menu callback\n     */\n    public final Callback<ContextMenuParameter, ContextMenu> getContextMenuCallback() {\n        return contextMenuCallbackProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #contextMenuCallbackProperty()}.\n     *\n     * @param callback the context menu callback\n     */\n    public final void setContextMenuCallback(Callback<ContextMenuParameter, ContextMenu> callback) {\n        contextMenuCallbackProperty().set(callback);\n    }\n\n    /*\n     * Default calendar provider callback.\n     */\n    private final ObjectProperty<Callback<DateControl, Calendar>> defaultCalendarProvider = new SimpleObjectProperty<>(this, \"defaultCalendarProvider\");\n\n    /**\n     * The default calendar provider is responsible for returning a calendar\n     * that can be used to add a new entry. This way the user can add new\n     * entries by simply double-clicking inside the view without the need of\n     * first showing a calendar selection UI. This can be changed by setting a\n     * callback that prompts the user with a dialog.\n     *\n     * <h2>Code Example</h2>\n     * <p>\n     * The code shown below is the default implementation of this provider. It\n     * returns the first calendar of the first source. If no source is available\n     * it will return null.\n     * <pre>\n     * setDefaultCalendarProvider(control -&gt; {\n     * \tList&lt;CalendarSource&gt; sources = getCalendarSources();\n     * \tif (sources != null &amp;&amp; !sources.isEmpty()) {\n     * \t\tCalendarSource s = sources.get(0);\n     * \t\tList&lt;? extends Calendar&gt; calendars = s.getCalendars();\n     * \t\tif (calendars != null &amp;&amp; !calendars.isEmpty()) {\n     * \t\t\treturn calendars.get(0);\n     *        }\n     *    }\n     *\n     * \treturn null;\n     * });\n     * </pre>\n     *\n     * @return the default calendar provider callback\n     */\n    public final ObjectProperty<Callback<DateControl, Calendar>> defaultCalendarProviderProperty() {\n        return defaultCalendarProvider;\n    }\n\n    /**\n     * Returns the value of {@link #defaultCalendarProviderProperty()}.\n     *\n     * @return the default calendar provider\n     */\n    public final Callback<DateControl, Calendar> getDefaultCalendarProvider() {\n        return defaultCalendarProviderProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #defaultCalendarProviderProperty()}.\n     *\n     * @param provider the default calendar provider\n     */\n    public final void setDefaultCalendarProvider(Callback<DateControl, Calendar> provider) {\n        requireNonNull(provider);\n        defaultCalendarProviderProperty().set(provider);\n    }\n\n    private void updateCalendarList() {\n        List<Calendar> removedCalendars = new ArrayList<>(calendars);\n        List<Calendar> newCalendars = new ArrayList<>();\n        for (CalendarSource source : getCalendarSources()) {\n            for (Calendar calendar : source.getCalendars()) {\n                if (calendars.contains(calendar)) {\n                    removedCalendars.remove(calendar);\n                } else {\n                    newCalendars.add(calendar);\n                }\n            }\n            source.getCalendars().removeListener(weakUpdateCalendarListListener);\n            source.getCalendars().addListener(weakUpdateCalendarListListener);\n        }\n\n        calendars.addAll(newCalendars);\n        calendars.removeAll(removedCalendars);\n\n        removedCalendars.forEach(calendar -> calendarVisibilityMap.remove(calendar));\n    }\n\n    private abstract static class DetailsParameter {\n\n        private final InputEvent inputEvent;\n        private final DateControl dateControl;\n        private final Node owner;\n        private final double screenX;\n        private final double screenY;\n        private final Node node;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param inputEvent the input event that triggered the need for showing entry\n         *                   details (e.g. a mouse double click, or a context menu item\n         *                   selection)\n         * @param control    the control where the event occurred\n         * @param owner      a node that can be used as an owner for the dialog or\n         *                   popover\n         * @param screenX    the screen location where the event occurred\n         * @param screenY    the screen location where the event occurred\n         */\n        public DetailsParameter(InputEvent inputEvent, DateControl control, Node node, Node owner, double screenX, double screenY) {\n            this.inputEvent = inputEvent;\n            this.dateControl = requireNonNull(control);\n            this.node = requireNonNull(node);\n            this.owner = requireNonNull(owner);\n            this.screenX = screenX;\n            this.screenY = screenY;\n        }\n\n        /**\n         * Returns the node that should be used as the owner of a dialog /\n         * popover. We should not use the entry view as the owner of a dialog /\n         * popover because views come and go. We need something that lives\n         * longer. A good candidate will be the root node of the scene.\n         *\n         * @return an owner node for the detail dialog / popover\n         */\n        public Node getOwner() {\n            return owner;\n        }\n\n        /**\n         * Returns the node that will be used for calculating the position\n         * of the detail dialog / popover. Popovers will point an arrow at the\n         * given node.\n         *\n         * @return the annotated node\n         */\n        public Node getNode() {\n            return node;\n        }\n\n        /**\n         * The screen X location where the event occurred.\n         *\n         * @return the screen x location of the event\n         */\n        public double getScreenX() {\n            return screenX;\n        }\n\n        /**\n         * The screen Y location where the event occurred.\n         *\n         * @return the screen y location of the event\n         */\n        public double getScreenY() {\n            return screenY;\n        }\n\n        /**\n         * The input event that triggered the need for showing entry details\n         * (e.g. a mouse double click or a context menu item selection).\n         *\n         * @return the input event\n         */\n        public InputEvent getInputEvent() {\n            return inputEvent;\n        }\n\n        /**\n         * The date control where the event occurred.\n         *\n         * @return the date control\n         */\n        public DateControl getDateControl() {\n            return dateControl;\n        }\n    }\n\n    /**\n     * The parameter object passed to the entry details callback.\n     *\n     * @see DateControl#entryDetailsCallbackProperty()\n     */\n    public final static class EntryDetailsParameter extends DetailsParameter {\n\n        private final Entry<?> entry;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param inputEvent the input event that triggered the need for showing entry\n         *                   details (e.g. a mouse double click, or a context menu item\n         *                   selection)\n         * @param control    the control where the event occurred\n         * @param entry      the entry for which details are requested\n         * @param node       the node to which the popover will be placed relative too (when using popovers)\n         * @param owner      a node that can be used as an owner for the dialog or\n         *                   popover\n         * @param screenX    the screen location where the event occurred\n         * @param screenY    the screen location where the event occurred\n         */\n        public EntryDetailsParameter(InputEvent inputEvent, DateControl control, Entry<?> entry, Node node, Node owner, double screenX, double screenY) {\n            super(inputEvent, control, node, owner, screenX, screenY);\n            this.entry = entry;\n        }\n\n        /**\n         * The entry for which details are requested.\n         *\n         * @return the entry view\n         */\n        public Entry<?> getEntry() {\n            return entry;\n        }\n    }\n\n    /**\n     * The parameter object passed to the date details callback.\n     *\n     * @see DateControl#dateDetailsCallbackProperty()\n     */\n    public final static class DateDetailsParameter extends DetailsParameter {\n\n        private final LocalDate localDate;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param inputEvent the input event that triggered the need for showing entry\n         *                   details (e.g. a mouse double click, or a context menu item\n         *                   selection)\n         * @param control    the control where the event occurred\n         * @param date       the date for which details are required\n         * @param node       the annotated node (popover will point at it with an arrow)\n         * @param owner      a node that can be used as an owner for the dialog or popover\n         * @param screenX    the screen location where the event occurred\n         * @param screenY    the screen location where the event occurred\n         */\n        public DateDetailsParameter(InputEvent inputEvent, DateControl control, Node node, Node owner, LocalDate date, double screenX, double screenY) {\n            super(inputEvent, control, node, owner, screenX, screenY);\n            this.localDate = requireNonNull(date);\n        }\n\n        /**\n         * The date for which details are required.\n         *\n         * @return the date\n         */\n        public LocalDate getLocalDate() {\n            return localDate;\n        }\n    }\n\n    private final ObjectProperty<Callback<DateDetailsParameter, Boolean>> dateDetailsCallback = new SimpleObjectProperty<>(this, \"dateDetailsCallback\");\n\n    /**\n     * A callback used for showing the details of a given date. The default\n     * implementation of this callback displays a small {@link PopOver} but\n     * applications might as well display a large dialog where the user can\n     * freely edit the date.\n     *\n     * <h2>Code Example</h2> The code below shows the default implementation\n     * used by all date controls. It delegates to a private method that shows\n     * the popover.\n     *\n     * <pre>\n     * setDateDetailsCallback(param -&gt; {\n     * \tInputEvent evt = param.getInputEvent();\n     * \tif (evt instanceof MouseEvent) {\n     * \t\tMouseEvent mouseEvent = (MouseEvent) evt;\n     * \t\tif (mouseEvent.getClickCount() == 1) {\n     * \t\t\tshowDateDetails(param.getOwner(), param.getLocalDate());\n     * \t\t\treturn true;\n     *        }\n     *    }\n     *\n     * \treturn false;\n     * });\n     * </pre>\n     *\n     * @return the callback for showing details for a given date\n     */\n    public final ObjectProperty<Callback<DateDetailsParameter, Boolean>> dateDetailsCallbackProperty() {\n        return dateDetailsCallback;\n    }\n\n    /**\n     * Sets the value of {@link #dateDetailsCallbackProperty()}.\n     *\n     * @param callback the date details callback\n     */\n    public final void setDateDetailsCallback(Callback<DateDetailsParameter, Boolean> callback) {\n        requireNonNull(callback);\n        dateDetailsCallbackProperty().set(callback);\n    }\n\n    /**\n     * Returns the value of {@link #dateDetailsCallbackProperty()}.\n     *\n     * @return the date details callback\n     */\n    public final Callback<DateDetailsParameter, Boolean> getDateDetailsCallback() {\n        return dateDetailsCallbackProperty().get();\n    }\n\n    private final ObjectProperty<Callback<EntryDetailsParameter, Boolean>> entryDetailsCallback = new SimpleObjectProperty<>(this, \"entryDetailsCallback\");\n\n    /**\n     * A callback used for showing the details of a given entry. The default\n     * implementation of this callback displays a small {@link PopOver} but\n     * applications might as well display a large dialog where the user can\n     * freely edit the entry.\n     *\n     * <h2>Code Example</h2> The code below shows the default implementation\n     * used by all date controls. It delegates to a private method that shows\n     * the popover.\n     *\n     * <pre>\n     * setEntryDetailsCallback(param -&gt; {\n     * \tInputEvent evt = param.getInputEvent();\n     * \tif (evt instanceof MouseEvent) {\n     * \t\tMouseEvent mouseEvent = (MouseEvent) evt;\n     * \t\tif (mouseEvent.getClickCount() == 2) {\n     * \t\t\tshowEntryDetails(param.getEntryView(), param.getOwner(), param.getScreenX(), param.getScreenY());\n     * \t\t\treturn true;\n     *        }\n     *    } else {\n     * \t\tshowEntryDetails(param.getEntryView(), param.getOwner(), param.getScreenX(), param.getScreenY());\n     * \t\treturn true;\n     *    }\n     *\n     * \treturn false;\n     * });\n     * </pre>\n     *\n     * @return the callback used for showing details for a given entry\n     */\n    public final ObjectProperty<Callback<EntryDetailsParameter, Boolean>> entryDetailsCallbackProperty() {\n        return entryDetailsCallback;\n    }\n\n    /**\n     * Sets the value of {@link #entryDetailsCallbackProperty()}.\n     *\n     * @param callback the entry details callback\n     */\n    public final void setEntryDetailsCallback(Callback<EntryDetailsParameter, Boolean> callback) {\n        requireNonNull(callback);\n        entryDetailsCallbackProperty().set(callback);\n    }\n\n    /**\n     * Returns the value of {@link #entryDetailsCallbackProperty()}.\n     *\n     * @return the entry details callback\n     */\n    public final Callback<EntryDetailsParameter, Boolean> getEntryDetailsCallback() {\n        return entryDetailsCallbackProperty().get();\n    }\n\n    ////////////\n\n    /**\n     * The parameter object passed to the entry details popover content\n     * callback.\n     *\n     * @see DateControl#entryDetailsPopOverContentCallbackProperty()\n     */\n    public final static class EntryDetailsPopOverContentParameter {\n\n        private final DateControl dateControl;\n\n        private final Node node;\n\n        private final Entry<?> entry;\n\n        private final PopOver popOver;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param popOver the popover for which details will be created\n         * @param control the control where the event occurred\n         * @param node    the node where the event occurred\n         * @param entry   the entry for which details will be shown\n         */\n        public EntryDetailsPopOverContentParameter(PopOver popOver, DateControl control, Node node, Entry<?> entry) {\n            this.popOver = requireNonNull(popOver);\n            this.dateControl = requireNonNull(control);\n            this.node = requireNonNull(node);\n            this.entry = requireNonNull(entry);\n        }\n\n        /**\n         * Returns the popover in which the content will be shown.\n         *\n         * @return the popover\n         */\n        public PopOver getPopOver() {\n            return popOver;\n        }\n\n        /**\n         * The date control where the popover was requested.\n         *\n         * @return the date control\n         */\n        public DateControl getDateControl() {\n            return dateControl;\n        }\n\n        /**\n         * The node for which the popover was requested.\n         *\n         * @return the node\n         */\n        public Node getNode() {\n            return node;\n        }\n\n        /**\n         * The entry for which the popover was requested.\n         *\n         * @return the entry\n         */\n        public Entry<?> getEntry() {\n            return entry;\n        }\n    }\n\n    private final ObjectProperty<Callback<EntryDetailsPopOverContentParameter, Node>> entryDetailsPopoverContentCallback = new SimpleObjectProperty<>(this, \"entryDetailsPopoverContentCallback\");\n\n    /**\n     * Stores a callback for creating the content of the popover.\n     *\n     * @return the popover content callback\n     */\n    public final ObjectProperty<Callback<EntryDetailsPopOverContentParameter, Node>> entryDetailsPopOverContentCallbackProperty() {\n        return entryDetailsPopoverContentCallback;\n    }\n\n    /**\n     * Sets the value of {@link #entryDetailsPopOverContentCallbackProperty()}.\n     *\n     * @param callback the entry details popover content callback\n     */\n    public final void setEntryDetailsPopOverContentCallback(Callback<EntryDetailsPopOverContentParameter, Node> callback) {\n        requireNonNull(callback);\n        entryDetailsPopOverContentCallbackProperty().set(callback);\n    }\n\n    /**\n     * Returns the value of\n     * {@link #entryDetailsPopOverContentCallbackProperty()}.\n     *\n     * @return the entry details popover content callback\n     */\n    public final Callback<EntryDetailsPopOverContentParameter, Node> getEntryDetailsPopOverContentCallback() {\n        return entryDetailsPopOverContentCallbackProperty().get();\n    }\n\n    ///////////\n\n    private final ObjectProperty<LocalDate> today = new SimpleObjectProperty<>(this, \"today\", LocalDate.now());\n\n    /**\n     * Stores the date that is considered to represent \"today\". This property is\n     * initialized with {@link LocalDate#now()} but can be any date.\n     *\n     * @return the date representing \"today\"\n     */\n    public final ObjectProperty<LocalDate> todayProperty() {\n        return today;\n    }\n\n    /**\n     * Sets the value of {@link #todayProperty()}.\n     *\n     * @param date the date representing \"today\"\n     */\n    public final void setToday(LocalDate date) {\n        requireNonNull(date);\n        todayProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #todayProperty()}.\n     *\n     * @return the date representing \"today\"\n     */\n    public final LocalDate getToday() {\n        return todayProperty().get();\n    }\n\n    private final BooleanProperty showToday = new SimpleBooleanProperty(this, \"showToday\", true);\n\n    /**\n     * A flag used to indicate that the view will mark the area that represents\n     * the value of {@link #todayProperty()}. By default, this area will be\n     * filled with a different color (red) than the rest (white).\n     * <img src=\"doc-files/all-day-view-today.png\" alt=\"All Day View Today\">\n     *\n     * @return true if today will be shown differently\n     */\n    public final BooleanProperty showTodayProperty() {\n        return showToday;\n    }\n\n    /**\n     * Returns the value of {@link #showTodayProperty()}.\n     *\n     * @return true if today will be highlighted visually\n     */\n    public final boolean isShowToday() {\n        return showTodayProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showTodayProperty()}.\n     *\n     * @param show if true today will be highlighted visually\n     */\n    public final void setShowToday(boolean show) {\n        showTodayProperty().set(show);\n    }\n\n    private final BooleanProperty showNoonMarker = new SimpleBooleanProperty(this, \"showNoonMarker\", true);\n\n    public final boolean isShowNoonMarker() {\n        return showNoonMarker.get();\n    }\n\n    /**\n     * A property used to indicate whether the day view should mark noon with a special\n     * marker.\n     *\n     * @return true if noon will be marked in a special way\n     */\n    public final BooleanProperty showNoonMarkerProperty() {\n        return showNoonMarker;\n    }\n\n    public final void setShowNoonMarker(boolean showNoonMarker) {\n        this.showNoonMarker.set(showNoonMarker);\n    }\n\n    private final ObjectProperty<LocalDate> date = new SimpleObjectProperty<>(this, \"date\", LocalDate.now());\n\n    /**\n     * The date that needs to be shown by the date control. This property is\n     * initialized with {@link LocalDate#now()}.\n     *\n     * @return the date shown by the control\n     */\n    public final ObjectProperty<LocalDate> dateProperty() {\n        return date;\n    }\n\n    /**\n     * Sets the value of {@link #dateProperty()}.\n     *\n     * @param date the date shown by the control\n     */\n    public final void setDate(LocalDate date) {\n        requireNonNull(date);\n        dateProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #dateProperty()}.\n     *\n     * @return the date shown by the control\n     */\n    public final LocalDate getDate() {\n        return dateProperty().get();\n    }\n\n\n    private final BooleanProperty enableTimeZoneSupport = new SimpleBooleanProperty(this, \"enableTimeZoneSupport\", false);\n\n    public final boolean isEnableTimeZoneSupport() {\n        return enableTimeZoneSupport.get();\n    }\n\n    /**\n     * Enables or disables user options to work with different time zones.\n     *\n     * @return true if time zone support is enabled\n     * @see DateControl#zoneIdProperty()\n     * @see Entry#zoneIdProperty()\n     * @see Interval#getZoneId()\n     */\n    public final BooleanProperty enableTimeZoneSupportProperty() {\n        return enableTimeZoneSupport;\n    }\n\n    public final void setEnableTimeZoneSupport(boolean enableTimeZoneSupport) {\n        this.enableTimeZoneSupport.set(enableTimeZoneSupport);\n    }\n\n    private final ObjectProperty<ZoneId> zoneId = new SimpleObjectProperty<>(this, \"zoneId\", ZoneId.systemDefault());\n\n    /**\n     * The time zone used by the date control. Entries and date controls might\n     * use different time zones resulting in different layout of entry views.\n     * <p>\n     * #see {@link Entry#zoneIdProperty()}\n     *\n     * @return the time zone used by the date control for calculating entry view\n     * layouts\n     */\n    public final ObjectProperty<ZoneId> zoneIdProperty() {\n        return zoneId;\n    }\n\n    /**\n     * Sets the value of {@link #zoneIdProperty()}.\n     *\n     * @param zoneId the time zone\n     */\n    public final void setZoneId(ZoneId zoneId) {\n        requireNonNull(zoneId);\n        zoneIdProperty().set(zoneId);\n    }\n\n    /**\n     * Returns the value of {@link #zoneIdProperty()}.\n     *\n     * @return the time zone\n     */\n    public final ZoneId getZoneId() {\n        return zoneIdProperty().get();\n    }\n\n    private final ObjectProperty<LocalTime> time = new SimpleObjectProperty<>(this, \"time\", LocalTime.now());\n\n    /**\n     * Stores a time that can be visualized, e.g. the thin line in\n     * {@link DayView} representing the current time.\n     *\n     * @return the current time\n     */\n    public final ObjectProperty<LocalTime> timeProperty() {\n        return time;\n    }\n\n    /**\n     * Sets the value of {@link #timeProperty()}.\n     *\n     * @param time the current time\n     */\n    public final void setTime(LocalTime time) {\n        requireNonNull(time);\n        timeProperty().set(time);\n    }\n\n    /**\n     * Returns the value of {@link #timeProperty()}.\n     *\n     * @return the current time\n     */\n    public final LocalTime getTime() {\n        return timeProperty().get();\n    }\n\n    /**\n     * Returns the zoned date time version of the current time.\n     *\n     * @return the zoned date time version of the current time property\n     * @see #getTime()\n     */\n    public final ZonedDateTime getZonedDateTime() {\n        return ZonedDateTime.of(getDate(), getTime(), getZoneId());\n    }\n\n    private final ObjectProperty<LocalTime> startTime = new SimpleObjectProperty<>(this, \"startTime\", LocalTime.of(6, 0));\n\n    /**\n     * A start time used to limit the time interval shown by the control. The\n     * {@link DayView} uses this property and the {@link #endTimeProperty()} to\n     * support the concept of \"early\" and \"late\" hours. These hours can be\n     * hidden if required.\n     *\n     * @return the start time\n     */\n    public final ObjectProperty<LocalTime> startTimeProperty() {\n        return startTime;\n    }\n\n    /**\n     * Returns the value of {@link #startTimeProperty()}.\n     *\n     * @return the start time\n     */\n    public final LocalTime getStartTime() {\n        return startTimeProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #startTimeProperty()}.\n     *\n     * @param time the start time\n     */\n    public final void setStartTime(LocalTime time) {\n        startTimeProperty().set(time);\n    }\n\n    private final ObjectProperty<LocalTime> endTime = new SimpleObjectProperty<>(this, \"endTime\", LocalTime.of(22, 0));\n\n    /**\n     * An end time used to limit the time interval shown by the control. The\n     * {@link DayView} uses this property and the {@link #startTimeProperty()}\n     * to support the concept of \"early\" and \"late\" hours. These hours can be\n     * hidden if required.\n     *\n     * @return the end time\n     */\n    public final ObjectProperty<LocalTime> endTimeProperty() {\n        return endTime;\n    }\n\n    /**\n     * Returns the value of {@link #endTimeProperty()}.\n     *\n     * @return the end time\n     */\n    public final LocalTime getEndTime() {\n        return endTimeProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #endTimeProperty()}.\n     *\n     * @param time the end time\n     */\n    public final void setEndTime(LocalTime time) {\n        endTimeProperty().set(time);\n    }\n\n    private final ObjectProperty<WeekFields> weekFields = new SimpleObjectProperty<>(this, \"weekFields\", WeekFields.of(Locale.getDefault()));\n\n    /**\n     * Week fields are used to determine the first day of a week (e.g. \"Monday\"\n     * in Germany or \"Sunday\" in the US). It is also used to calculate the week\n     * number as the week fields determine how many days are needed in the first\n     * week of a year. This property is initialized with {@link WeekFields#ISO}.\n     *\n     * @return the week fields\n     */\n    public final ObjectProperty<WeekFields> weekFieldsProperty() {\n        return weekFields;\n    }\n\n    /**\n     * Sets the value of {@link #weekFieldsProperty()}.\n     *\n     * @param weekFields the new week fields\n     */\n    public final void setWeekFields(WeekFields weekFields) {\n        requireNonNull(weekFields);\n        weekFieldsProperty().set(weekFields);\n    }\n\n    /**\n     * Returns the value of {@link #weekFieldsProperty()}.\n     *\n     * @return the week fields\n     */\n    public final WeekFields getWeekFields() {\n        return weekFieldsProperty().get();\n    }\n\n    /**\n     * A convenience method to look up the first day of the week (\"Monday\" in\n     * Germany, \"Sunday\" in the US). This method delegates to\n     * {@link WeekFields#getFirstDayOfWeek()}.\n     *\n     * @return the first day of the week\n     * @see #weekFieldsProperty()\n     */\n    public final DayOfWeek getFirstDayOfWeek() {\n        return getWeekFields().getFirstDayOfWeek();\n    }\n\n    private final ReadOnlyListWrapper<Calendar> calendars = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());\n\n    /**\n     * A list that contains all calendars found in all calendar sources\n     * currently attached to this date control. This is a convenience list that\n     * \"flattens\" the two level structure of sources and their calendars. It is\n     * a read-only list because calendars can not be added directly to a date\n     * control. Instead, they are added to calendar sources and those sources are\n     * then added to the control.\n     *\n     * @return the list of all calendars shown by this control\n     * @see #getCalendarSources()\n     */\n    public final ReadOnlyListProperty<Calendar> calendarsProperty() {\n        return calendars.getReadOnlyProperty();\n    }\n\n    private final ObservableList<Calendar> unmodifiableCalendars = FXCollections.unmodifiableObservableList(calendars.get());\n\n    /**\n     * Returns the value of {@link #calendarsProperty()}.\n     *\n     * @return the list of all calendars shown by this control\n     */\n    public final ObservableList<Calendar> getCalendars() {\n        return unmodifiableCalendars;\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections.observableArrayList();\n\n    /**\n     * The list of all calendar sources attached to this control. The calendars\n     * found in all sources are also added to the read-only list that can be\n     * retrieved by calling {@link #getCalendars()}.\n     *\n     * @return the calendar sources\n     * @see #calendarSourceFactoryProperty()\n     */\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ObjectProperty<SelectionMode> selectionMode = new SimpleObjectProperty<>(this, \"selectionMode\", SelectionMode.MULTIPLE);\n\n    /**\n     * Stores the selection mode. All date controls support single and multiple\n     * selections.\n     *\n     * @return the selection mode\n     * @see SelectionMode\n     */\n    public final ObjectProperty<SelectionMode> selectionModeProperty() {\n        return selectionMode;\n    }\n\n    /**\n     * Sets the value of {@link #selectionModeProperty()}.\n     *\n     * @param mode the selection mode (single, multiple)\n     */\n    public final void setSelectionMode(SelectionMode mode) {\n        requireNonNull(mode);\n        selectionModeProperty().set(mode);\n    }\n\n    /**\n     * Returns the value of {@link #selectionModeProperty()}.\n     *\n     * @return the selection mode (single, multiple)\n     */\n    public final SelectionMode getSelectionMode() {\n        return selectionModeProperty().get();\n    }\n\n    private final ObservableSet<Entry<?>> selections = FXCollections.observableSet();\n\n    /**\n     * Stores the currently selected entries.\n     *\n     * @return the set of currently selected entries\n     */\n    public final ObservableSet<Entry<?>> getSelections() {\n        return selections;\n    }\n\n    /**\n     * Adds the given entry to the set of currently selected entries.\n     *\n     * @param entry the selected entries\n     * @see #deselect(Entry)\n     * @see #getSelections()\n     */\n    public final void select(Entry<?> entry) {\n        requireNonNull(entry);\n        selections.add(entry);\n    }\n\n    /**\n     * Removes the given entry from the set of currently selected entries.\n     *\n     * @param entry the selected entries\n     * @see #select(Entry)\n     * @see #getSelections()\n     */\n    public final void deselect(Entry<?> entry) {\n        requireNonNull(entry);\n        selections.remove(entry);\n    }\n\n    /**\n     * Clears the current selection of entries.\n     */\n    public final void clearSelection() {\n        getSelections().clear();\n    }\n\n    private final ObjectProperty<VirtualGrid> virtualGrid = new SimpleObjectProperty<>(this, \"virtualGrid\", new VirtualGrid(Messages.getString(\"DateControl.DEFAULT_VIRTUAL_GRID_NAME\"), Messages.getString(\"DateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME\"), ChronoUnit.MINUTES, 15));\n\n    /**\n     * A virtual grid used for snapping to invisible grid lines while editing\n     * calendar entries. Using a virtual grid makes it easier to edit entries so\n     * that they all start at exactly the same time. The default grid is set to\n     * \"5 Minutes\". {@link VirtualGrid#OFF} can be used to completely disable\n     * the grid.\n     *\n     * @return the virtual grid\n     */\n    public final ObjectProperty<VirtualGrid> virtualGridProperty() {\n        return virtualGrid;\n    }\n\n    /**\n     * Returns the value of {@link #virtualGridProperty()}.\n     *\n     * @return the currently active grid\n     */\n    public final VirtualGrid getVirtualGrid() {\n        return virtualGridProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #virtualGridProperty()}.\n     *\n     * @param grid the grid\n     */\n    public final void setVirtualGrid(VirtualGrid grid) {\n        requireNonNull(grid);\n        virtualGridProperty().set(grid);\n    }\n\n    private final ObjectProperty<LocalTime> requestedTime = new SimpleObjectProperty<>(this, \"requestedTime\", LocalTime.now());\n\n    /**\n     * Stores the time that the application wants to show in its date controls\n     * when the UI opens up. Most applications will normally set this time to\n     * {@link LocalTime#now()}.\n     *\n     * @return the requested time\n     */\n    public final ObjectProperty<LocalTime> requestedTimeProperty() {\n        return requestedTime;\n    }\n\n    /**\n     * Sets the value of {@link #requestedTimeProperty()}.\n     *\n     * @param time the requested time\n     */\n    public final void setRequestedTime(LocalTime time) {\n        requestedTimeProperty().set(time);\n    }\n\n    /**\n     * Returns the value of {@link #requestedTimeProperty()}.\n     *\n     * @return the requested time\n     */\n    public final LocalTime getRequestedTime() {\n        return requestedTimeProperty().get();\n    }\n\n    /**\n     * Supported layout strategies by the {@link DayView}.\n     */\n    public enum Layout {\n\n        /**\n         * The standard layout lays out calendar entries in the most efficient\n         * way without distinguishing between different calendars. This is the\n         * layout found in most calendar software.\n         */\n        STANDARD,\n\n        /**\n         * The swimlane layout creates virtual columns, one for each calendar.\n         * The entries of the calendars are shown in their own column. This\n         * layout strategy is often found in resource booking systems (e.g. one\n         * calendar per room / per person / per truck).\n         */\n        SWIMLANE\n    }\n\n    private final ObjectProperty<Layout> layout = new SimpleObjectProperty<>(this, \"layout\", Layout.STANDARD);\n\n    /**\n     * Stores the strategy used by the view to lay out the entries of several\n     * calendars at once. The standard layout ignores the source calendar of an\n     * entry and finds the next available place in the UI that satisfies the\n     * time bounds of the entry. The {@link Layout#SWIMLANE} strategy allocates\n     * a separate column for each calendar and resolves overlapping entry\n     * conflicts within that column. Swim lanes are especially useful for\n     * resource booking systems (rooms, people, trucks).\n     *\n     * @return the layout strategy of the view\n     */\n    public final ObjectProperty<Layout> layoutProperty() {\n        return layout;\n    }\n\n    /**\n     * Sets the value of {@link #layoutProperty()}.\n     *\n     * @param layout the layout\n     */\n    public final void setLayout(Layout layout) {\n        requireNonNull(layout);\n        layoutProperty().set(layout);\n    }\n\n    /**\n     * Returns the value of {@link #layoutProperty()}.\n     *\n     * @return the layout strategy\n     */\n    public final Layout getLayout() {\n        return layoutProperty().get();\n    }\n\n    private final ObservableSet<DayOfWeek> weekendDays = FXCollections.observableSet();\n\n    /**\n     * Returns the days of the week that are considered to be weekend days, for\n     * example Saturday and Sunday, or Friday and Saturday.\n     *\n     * @return the weekend days\n     */\n    public ObservableSet<DayOfWeek> getWeekendDays() {\n        return weekendDays;\n    }\n\n    /**\n     * Makes the control go forward in time by adding one or more days to the\n     * current date. Subclasses override this method to adjust it to their\n     * needs, e.g. the {@link DetailedWeekView} adds the number of days found in\n     * {@link DetailedWeekView#getNumberOfDays()}.\n     *\n     * @see #dateProperty()\n     */\n    public void goForward() {\n        setDate(getDate().plusDays(1));\n    }\n\n    /**\n     * Makes the control go forward in time by removing one or more days from\n     * the current date. Subclasses override this method to adjust it to their\n     * needs, e.g. the {@link DetailedWeekView} removes the number of days found in\n     * {@link DetailedWeekView#getNumberOfDays()}.\n     *\n     * @see #dateProperty()\n     */\n    public void goBack() {\n        setDate(getDate().minusDays(1));\n    }\n\n    /**\n     * Makes the control go to \"today\".\n     *\n     * @see #dateProperty()\n     * @see #todayProperty()\n     */\n    public void goToday() {\n        setDate(getToday());\n    }\n\n    /**\n     * Finds the first view that represents the given entry.\n     *\n     * @param entry the entry\n     * @return the view\n     */\n    public final EntryViewBase<?> findEntryView(Entry<?> entry) {\n        requireNonNull(entry);\n        return doFindEntryView(this, entry);\n    }\n\n    private EntryViewBase<?> doFindEntryView(Parent parent, Entry<?> entry) {\n        EntryViewBase<?> result = null;\n\n        for (Node node : parent.getChildrenUnmodifiable()) {\n            if (node instanceof EntryViewBase) {\n                EntryViewBase<?> base = (EntryViewBase<?>) node;\n                if (base.getEntry().equals(entry)) {\n                    result = base;\n                    break;\n                }\n            } else if (node instanceof Parent) {\n                result = doFindEntryView((Parent) node, entry);\n                if (result != null) {\n                    break;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    private final WeakList<DateControl> boundDateControls = new WeakList<>();\n\n    /**\n     * Returns all data controls that are bound to this control.\n     *\n     * @return the bound date controls / sub controls / children controls\n     */\n    public final WeakList<DateControl> getBoundDateControls() {\n        return boundDateControls;\n    }\n\n    /**\n     * Unbinds all bound date controls.\n     *\n     * @see #bind(DateControl, boolean)\n     * @see #unbind(DateControl)\n     */\n    public final void unbindAll() {\n        WeakList<DateControl> controls = getBoundDateControls();\n        for (DateControl next : controls) {\n            if (next != null) {\n                unbind(next);\n            }\n        }\n    }\n\n    // hyperlink support\n    private final BooleanProperty enableHyperlinks = new SimpleBooleanProperty(this, \"enableHyperlinks\", true);\n\n    /**\n     * A property used to control whether the control allows the user to click on it or an element\n     * inside it in order to \"jump\" to another screen with more detail. Example: in the {@link CalendarView}\n     * the user can click on the \"day of month\" label of a cell inside the {@link MonthSheetView} in\n     * order to switch to the {@link DayPage} where the user will see all entries scheduled for that day.\n     *\n     * @return true if the support for hyperlinks is enabled\n     */\n    public final BooleanProperty enableHyperlinksProperty() {\n        return enableHyperlinks;\n    }\n\n    /**\n     * Sets the value of the {@link #enableHyperlinksProperty()}.\n     *\n     * @param enable if true the hyperlink support will be enabled\n     */\n    public final void setEnableHyperlinks(boolean enable) {\n        this.enableHyperlinks.set(enable);\n    }\n\n    /**\n     * Returns the value of the {@link #enableHyperlinksProperty()}.\n     *\n     * @return true if the hyperlink support is enabled\n     */\n    public final boolean isEnableHyperlinks() {\n        return enableHyperlinks.get();\n    }\n\n    private final BooleanProperty editAvailability = new SimpleBooleanProperty(this, \"editAvailability\");\n\n    public boolean isEditAvailability() {\n        return editAvailability.get();\n    }\n\n    /**\n     * A flag used to signal whether the user is allowed to modify the availability calendar. If he is\n     * allowed then the user can click on \"time slots\" based on the {@link #availabilityGridProperty()}.\n     *\n     * @return true if the user can edit availability\n     */\n    public BooleanProperty editAvailabilityProperty() {\n        return editAvailability;\n    }\n\n    public void setEditAvailability(boolean editAvailability) {\n        this.editAvailability.set(editAvailability);\n    }\n\n    private final ObjectProperty<Calendar> availabilityCalendar = new SimpleObjectProperty<>(this, \"availabilityCalendar\", new Calendar());\n\n    public final Calendar getAvailabilityCalendar() {\n        return availabilityCalendar.get();\n    }\n\n    /**\n     * A background calendar used to display \"availability\" of a resource.\n     *\n     * @return the background calendar used for this view\n     */\n    public final ObjectProperty<Calendar> availabilityCalendarProperty() {\n        return availabilityCalendar;\n    }\n\n    public final void setAvailabilityCalendar(Calendar calendar) {\n        this.availabilityCalendar.set(calendar);\n    }\n\n    private final ObjectProperty<VirtualGrid> availabilityGrid = new SimpleObjectProperty<>(this, \"availabilityGrid\", new VirtualGrid(\"30 Minutes\", \"30 Minutes\", ChronoUnit.MINUTES, 30));\n\n    public final VirtualGrid getAvailabilityGrid() {\n        return availabilityGrid.get();\n    }\n\n    /**\n     * Defines a virtual grid to be used for working with the availability calendar.\n     *\n     * @return the availability calendar grid size\n     * @see #availabilityCalendarProperty()\n     */\n    public final ObjectProperty<VirtualGrid> availabilityGridProperty() {\n        return availabilityGrid;\n    }\n\n    public final void setAvailabilityGrid(VirtualGrid availabilityGrid) {\n        this.availabilityGrid.set(availabilityGrid);\n    }\n\n    private final ObjectProperty<Paint> availabilityFill = new SimpleObjectProperty<>(this, \"availabilityFill\", Color.rgb(0, 0, 0, .1));\n\n    public final Paint getAvailabilityFill() {\n        return availabilityFill.get();\n    }\n\n    /**\n     * The color used for drawing the availability background information behind the calendar\n     * entries.\n     *\n     * @return the color used for filling \"unavailable\" time slots\n     */\n    public final ObjectProperty<Paint> availabilityFillProperty() {\n        return availabilityFill;\n    }\n\n    public final void setAvailabilityFill(Paint availabilityFill) {\n        this.availabilityFill.set(availabilityFill);\n    }\n\n    private final BooleanProperty showDetailsUponEntryCreation = new SimpleBooleanProperty(this, \"showDetailsUponEntryCreation\", true);\n\n    public final boolean isShowDetailsUponEntryCreation() {\n        return showDetailsUponEntryCreation.get();\n    }\n\n    /**\n     * Determines if the {@link #entryDetailsCallbackProperty()} will be used to display\n     * a dialog for a newly added calendar entry.\n     *\n     * @return true if the date control shows a dialog immediately after a new entry was added by the user\n     */\n    public final BooleanProperty showDetailsUponEntryCreationProperty() {\n        return showDetailsUponEntryCreation;\n    }\n\n    public final void setShowDetailsUponEntryCreation(boolean showDetailsUponEntryCreation) {\n        this.showDetailsUponEntryCreation.set(showDetailsUponEntryCreation);\n    }\n\n    /**\n     * Binds several properties of the given date control to the same properties\n     * of this control. This kind of binding is needed to create UIs with nested\n     * date controls. The {@link CalendarView} for example consists of several\n     * pages. Each page is a date control that consists of several other date\n     * controls. The {@link DayPage} consists of the {@link AgendaView}, a\n     * single {@link DayView}, and a {@link YearMonthView} . All of these\n     * controls are bound to each other so that the application can simply\n     * change properties on the {@link CalendarView} without worrying about the\n     * nested controls.\n     *\n     * @param otherControl the control that will be bound to this control\n     * @param bindDate     determines if the date property will also be bound\n     */\n    public final void bind(DateControl otherControl, boolean bindDate) {\n        requireNonNull(otherControl);\n        boundDateControls.add(otherControl);\n\n        // bind collections\n        Bindings.bindContentBidirectional(otherControl.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        Bindings.bindContentBidirectional(otherControl.getCalendarSources(), getCalendarSources());\n        Bindings.bindContentBidirectional(otherControl.getSelections(), getSelections());\n        Bindings.bindContentBidirectional(otherControl.getWeekendDays(), getWeekendDays());\n        Bindings.bindContentBidirectional(otherControl.getAvailableZoneIds(), getAvailableZoneIds());\n\n        // bind properties\n        Bindings.bindBidirectional(otherControl.suspendUpdatesProperty(), suspendUpdatesProperty());\n        Bindings.bindBidirectional(otherControl.virtualGridProperty(), virtualGridProperty());\n        Bindings.bindBidirectional(otherControl.draggedEntryProperty(), draggedEntryProperty());\n        Bindings.bindBidirectional(otherControl.requestedTimeProperty(), requestedTimeProperty());\n\n        Bindings.bindBidirectional(otherControl.selectionModeProperty(), selectionModeProperty());\n        Bindings.bindBidirectional(otherControl.weekFieldsProperty(), weekFieldsProperty());\n        Bindings.bindBidirectional(otherControl.layoutProperty(), layoutProperty());\n        if (bindDate) {\n            Bindings.bindBidirectional(otherControl.dateProperty(), dateProperty());\n        }\n\n        Bindings.bindBidirectional(otherControl.todayProperty(), todayProperty());\n        Bindings.bindBidirectional(otherControl.zoneIdProperty(), zoneIdProperty());\n\n        Bindings.bindBidirectional(otherControl.startTimeProperty(), startTimeProperty());\n        Bindings.bindBidirectional(otherControl.endTimeProperty(), endTimeProperty());\n        Bindings.bindBidirectional(otherControl.timeProperty(), timeProperty());\n        Bindings.bindBidirectional(otherControl.usagePolicyProperty(), usagePolicyProperty());\n        Bindings.bindBidirectional(otherControl.enableTimeZoneSupportProperty(), enableTimeZoneSupportProperty());\n        Bindings.bindBidirectional(otherControl.showDetailsUponEntryCreationProperty(), showDetailsUponEntryCreationProperty());\n        Bindings.bindBidirectional(otherControl.showNoonMarkerProperty(), showNoonMarkerProperty());\n        Bindings.bindBidirectional(otherControl.showTodayProperty(), showTodayProperty());\n\n        Bindings.bindBidirectional(otherControl.editAvailabilityProperty(), editAvailabilityProperty());\n        Bindings.bindBidirectional(otherControl.availabilityCalendarProperty(), availabilityCalendarProperty());\n        Bindings.bindBidirectional(otherControl.availabilityGridProperty(), availabilityGridProperty());\n        Bindings.bindBidirectional(otherControl.availabilityFillProperty(), availabilityFillProperty());\n        Bindings.bindBidirectional(otherControl.createEntryClickCountProperty(), createEntryClickCountProperty());\n\n        // bind callbacks\n        Bindings.bindBidirectional(otherControl.contextMenuCallbackProperty(), contextMenuCallbackProperty());\n        Bindings.bindBidirectional(otherControl.calendarSourceFactoryProperty(), calendarSourceFactoryProperty());\n        Bindings.bindBidirectional(otherControl.entryDetailsPopOverContentCallbackProperty(), entryDetailsPopOverContentCallbackProperty());\n        Bindings.bindBidirectional(otherControl.entryEditPolicyProperty(), entryEditPolicyProperty());\n        Bindings.bindBidirectional(otherControl.entryDetailsCallbackProperty(), entryDetailsCallbackProperty());\n        Bindings.bindBidirectional(otherControl.dateDetailsCallbackProperty(), dateDetailsCallbackProperty());\n        Bindings.bindBidirectional(otherControl.entryContextMenuCallbackProperty(), entryContextMenuCallbackProperty());\n        Bindings.bindBidirectional(otherControl.entryFactoryProperty(), entryFactoryProperty());\n        Bindings.bindBidirectional(otherControl.defaultCalendarProviderProperty(), defaultCalendarProviderProperty());\n    }\n\n    /**\n     * Unbinds the given control from this control. Unbinding is done for all\n     * properties and observable lists that have previously been bound by the\n     * {@link #bind(DateControl, boolean)} method.\n     *\n     * @param otherControl the control to unbind\n     */\n    public final void unbind(DateControl otherControl) {\n        requireNonNull(otherControl);\n\n        // important, otherwise we end up with a memory leak\n        otherControl.getCalendarVisibilityMap().clear();\n\n        // unbind collections\n        Bindings.unbindContentBidirectional(otherControl.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        Bindings.unbindContentBidirectional(otherControl.getCalendarSources(), getCalendarSources());\n        Bindings.unbindContentBidirectional(otherControl.getSelections(), getSelections());\n        Bindings.unbindContentBidirectional(otherControl.getWeekendDays(), getWeekendDays());\n        Bindings.unbindContentBidirectional(otherControl.getAvailableZoneIds(), getAvailableZoneIds());\n\n        // unbind properties\n        Bindings.unbindBidirectional(otherControl.suspendUpdatesProperty(), suspendUpdatesProperty());\n        Bindings.unbindBidirectional(otherControl.virtualGridProperty(), virtualGridProperty());\n        Bindings.unbindBidirectional(otherControl.draggedEntryProperty(), draggedEntryProperty());\n        Bindings.unbindBidirectional(otherControl.requestedTimeProperty(), requestedTimeProperty());\n\n        Bindings.unbindBidirectional(otherControl.selectionModeProperty(), selectionModeProperty());\n        Bindings.unbindBidirectional(otherControl.weekFieldsProperty(), weekFieldsProperty());\n        Bindings.unbindBidirectional(otherControl.layoutProperty(), layoutProperty());\n        Bindings.unbindBidirectional(otherControl.dateProperty(), dateProperty());\n\n        Bindings.unbindBidirectional(otherControl.todayProperty(), todayProperty());\n        Bindings.unbindBidirectional(otherControl.zoneIdProperty(), zoneIdProperty());\n\n        Bindings.unbindBidirectional(otherControl.startTimeProperty(), startTimeProperty());\n        Bindings.unbindBidirectional(otherControl.endTimeProperty(), endTimeProperty());\n        Bindings.unbindBidirectional(otherControl.timeProperty(), timeProperty());\n        Bindings.unbindBidirectional(otherControl.usagePolicyProperty(), usagePolicyProperty());\n        Bindings.unbindBidirectional(otherControl.enableTimeZoneSupportProperty(), enableTimeZoneSupportProperty());\n        Bindings.unbindBidirectional(otherControl.showDetailsUponEntryCreationProperty(), showDetailsUponEntryCreationProperty());\n        Bindings.unbindBidirectional(otherControl.showNoonMarkerProperty(), showNoonMarkerProperty());\n        Bindings.unbindBidirectional(otherControl.showTodayProperty(), showTodayProperty());\n\n        Bindings.unbindBidirectional(otherControl.editAvailabilityProperty(), editAvailabilityProperty());\n        Bindings.unbindBidirectional(otherControl.availabilityCalendarProperty(), availabilityCalendarProperty());\n        Bindings.unbindBidirectional(otherControl.availabilityGridProperty(), availabilityGridProperty());\n        Bindings.unbindBidirectional(otherControl.availabilityFillProperty(), availabilityFillProperty());\n        Bindings.unbindBidirectional(otherControl.createEntryClickCountProperty(), createEntryClickCountProperty());\n\n        // unbind callbacks\n        Bindings.unbindBidirectional(otherControl.contextMenuCallbackProperty(), contextMenuCallbackProperty());\n        Bindings.unbindBidirectional(otherControl.calendarSourceFactoryProperty(), calendarSourceFactoryProperty());\n        Bindings.unbindBidirectional(otherControl.entryDetailsCallbackProperty(), entryDetailsCallbackProperty());\n        Bindings.unbindBidirectional(otherControl.entryEditPolicyProperty(), entryEditPolicyProperty());\n\n        Bindings.unbindBidirectional(otherControl.entryDetailsPopOverContentCallbackProperty(), entryDetailsPopOverContentCallbackProperty());\n        Bindings.unbindBidirectional(otherControl.dateDetailsCallbackProperty(), dateDetailsCallbackProperty());\n        Bindings.unbindBidirectional(otherControl.entryContextMenuCallbackProperty(), entryContextMenuCallbackProperty());\n        Bindings.unbindBidirectional(otherControl.entryFactoryProperty(), entryFactoryProperty());\n        Bindings.unbindBidirectional(otherControl.defaultCalendarProviderProperty(), defaultCalendarProviderProperty());\n\n    }\n\n    private final BooleanProperty suspendUpdates = new SimpleBooleanProperty(this, \"suspendUpdates\", false);\n\n    /**\n     * A property that will suspend all updates to the view based on model changes. This feature\n     * comes in handy when performing large batch updates with many adds and / or removes of calendar\n     * entries. When this property is set to true the view will not add / remove / update any entry\n     * views. Once it is set back to false a single refresh will be executed.\n     *\n     * @return true if updates are suspended\n     */\n    public final BooleanProperty suspendUpdatesProperty() {\n        return suspendUpdates;\n    }\n\n    /**\n     * Returns the value of {@link #suspendUpdatesProperty()}.\n     *\n     * @return true if updates are suspended\n     */\n    public final boolean isSuspendUpdates() {\n        return suspendUpdates.get();\n    }\n\n    /**\n     * Sets the value of {@link #suspendUpdatesProperty()}.\n     *\n     * @param suspend if true updates are suspended\n     */\n    public final void setSuspendUpdates(boolean suspend) {\n        this.suspendUpdates.set(suspend);\n    }\n\n    // usage policy support\n\n    public enum Usage {\n        NONE,\n        VERY_LOW,\n        LOW,\n        MEDIUM,\n        HIGH,\n        VERY_HIGH\n    }\n\n    public final ObjectProperty<Callback<Integer, Usage>> usagePolicy = new SimpleObjectProperty<>(this, \"usagePolicy\");\n\n    /**\n     * A property used to store a policy that will be used to determine if a given number of entries\n     * indicates a low or high usage of that day. This policy is used by the {@link YearMonthView} to color\n     * the background of each day based on the \"usage\" of that day. The default implementation of this policy\n     * returns \"none\" for 0 entries, \"very low\" for 1 entry, \"low\" for 2 entries, \"medium\" for 3 entries,\n     * \"high\" for 4 entries, and \"very high\" for 5 entries or more.\n     *\n     * @return the usage policy\n     */\n    public final ObjectProperty<Callback<Integer, Usage>> usagePolicyProperty() {\n        return usagePolicy;\n    }\n\n    /**\n     * Sets the value of {@link #usagePolicyProperty()}.\n     *\n     * @param policy the new usage policy\n     */\n    public final void setUsagePolicy(Callback<Integer, Usage> policy) {\n        Objects.requireNonNull(policy);\n        this.usagePolicy.set(policy);\n    }\n\n    /**\n     * Returns the value of {@link #usagePolicyProperty()}.\n     *\n     * @return the new usage policy\n     */\n    public final Callback<Integer, Usage> getUsagePolicy() {\n        return usagePolicy.get();\n    }\n\n    private final ObservableList<ZoneId> availableZoneIds = FXCollections.observableArrayList();\n\n    /**\n     * A list of time zones / time zone IDs that will be available to the user\n     * in the date controls. This list is pre-populated with a small subset of\n     * all available Zone IDs.\n     *\n     * @return the list of available time zone IDs\n     */\n    public final ObservableList<ZoneId> getAvailableZoneIds() {\n        return availableZoneIds;\n    }\n\n    private final IntegerProperty createEntryClickCount = new SimpleIntegerProperty(this, \"createEntryClickCount\", 2);\n\n    public int getCreateEntryClickCount() {\n        return createEntryClickCount.get();\n    }\n\n    /**\n     * An integer that determines how many times the user has to perform a primary\n     * mouse button click in order to create a new entry.\n     *\n     * @return the number of mouse clicks required for creating a new entry\n     */\n    public IntegerProperty createEntryClickCountProperty() {\n        return createEntryClickCount;\n    }\n\n    public void setCreateEntryClickCount(int createEntryClickCount) {\n        this.createEntryClickCount.set(createEntryClickCount);\n    }\n\n    private static final String DATE_CONTROL_CATEGORY = \"Date Control\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(dateProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setDate((LocalDate) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getDate();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Date\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Date\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(layoutProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setLayout((Layout) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getLayout();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Layout.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Layout\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Layout\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(selectionModeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setSelectionMode((SelectionMode) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getSelectionMode();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return SelectionMode.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Selection Mode\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Selection Mode\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(todayProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setToday((LocalDate) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getToday();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Today\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Today\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTodayProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowToday((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowToday();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Today\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Highlight today\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(zoneIdProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setZoneId((ZoneId) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getZoneId();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return ZoneId.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Timezone\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Timezone\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(weekFieldsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setWeekFields((WeekFields) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getWeekFields();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return WeekFields.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Week Fields\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Week Fields (calendar standard)\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(timeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setTime((LocalTime) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getTime();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Time\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Time\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(startTimeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setStartTime((LocalTime) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getStartTime();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Start Time\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The first visible time at the top.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(endTimeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEndTime((LocalTime) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getEndTime();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"End Time\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The last visible time at the bottom.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(enableHyperlinksProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEnableHyperlinks((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEnableHyperlinks();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Hyperlinks\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Hyperlinks enabled / disabled\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.empty();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                if ((Boolean) value) {\n                    visibleLayersProperty().add(Layer.BASE);\n                } else {\n                    visibleLayersProperty().remove(Layer.BASE);\n                }\n            }\n\n            @Override\n            public Object getValue() {\n                return visibleLayersProperty().contains(Layer.BASE);\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Base Layer\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Base Layer visible / hidden\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.empty();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                if ((Boolean) value) {\n                    visibleLayersProperty().add(Layer.TOP);\n                } else {\n                    visibleLayersProperty().remove(Layer.TOP);\n                }\n            }\n\n            @Override\n            public Object getValue() {\n                return visibleLayersProperty().contains(Layer.TOP);\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Top Layer\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Top Layer visible / hidden\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showNoonMarkerProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowNoonMarker((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowNoonMarker();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Noon Marker\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show / hide markers for noon.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(editAvailabilityProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEditAvailability((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEditAvailability();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Edit Availability\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Allow editing of the availability calendar\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(enableTimeZoneSupportProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEnableTimeZoneSupport((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEnableTimeZoneSupport();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Enable Timezone Support\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Provide UI controls to work with timezones.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showDetailsUponEntryCreationProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowDetailsUponEntryCreation((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowDetailsUponEntryCreation();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Details upon Creation\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show the details dialog immediately after creating a new entry\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(createEntryClickCountProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setCreateEntryClickCount((Integer) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getCreateEntryClickCount();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Create Entry Click Count\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Set the number of clicks needed to create a new entry.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(availabilityFillProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setAvailabilityFill((Paint) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getAvailabilityFill();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Paint.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Availability Fill Color\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Availability Fill Color\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DATE_CONTROL_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DateSelectionModel.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class DateSelectionModel {\n\n    private LocalDate lastSelected;\n    private DateSelector selector;\n    private final ObservableList<LocalDate> selectedDates = FXCollections.observableArrayList();\n    private final ObservableList<LocalDate> unmodifiable = FXCollections.unmodifiableObservableList(selectedDates);\n    private final ObjectProperty<SelectionMode> selectionMode = new SimpleObjectProperty<>(this, \"selectionMode\");\n\n    public DateSelectionModel() {\n        super();\n        setSelectionMode(SelectionMode.MULTIPLE_DATES);\n    }\n\n    public final void select(LocalDate date) {\n        selector.select(date);\n    }\n\n    public final void selectRange(LocalDate start, LocalDate end) {\n        selector.selectRange(start, end);\n    }\n\n    public final void selectUntil(LocalDate date) {\n        selector.selectUntil(date);\n    }\n\n    public final void deselect(LocalDate date) {\n        selector.deselect(date);\n    }\n\n    public final void clearAndSelect(LocalDate date) {\n        selector.clearAndSelect(date);\n    }\n\n    public final void clear() {\n        selector.clear();\n    }\n\n    public final boolean isSelected(LocalDate date) {\n        return selectedDates.contains(date);\n    }\n\n    public final boolean isEmpty() {\n        return selectedDates.isEmpty();\n    }\n\n    public final ObservableList<LocalDate> getSelectedDates() {\n        return unmodifiable;\n    }\n\n    public final LocalDate getLastSelected() {\n        return lastSelected;\n    }\n\n    public final ObjectProperty<SelectionMode> selectionModeProperty() {\n        return selectionMode;\n    }\n\n    public final SelectionMode getSelectionMode() {\n        return selectionModeProperty().get();\n    }\n\n    public final void setSelectionMode(SelectionMode selectionMode) {\n        SelectionMode oldMode = getSelectionMode();\n        selectionModeProperty().set(Objects.requireNonNull(selectionMode));\n        if (oldMode != selectionMode) {\n            selector = DateSelector.createInstance(this);\n        }\n    }\n\n    public enum SelectionMode {\n\n        SINGLE_DATE,\n\n        SINGLE_DATE_RANGE,\n\n        MULTIPLE_DATES\n\n    }\n\n    private static abstract class DateSelector {\n\n        protected final DateSelectionModel model;\n\n        public DateSelector(DateSelectionModel model) {\n            this.model = model;\n        }\n\n        public abstract void select(LocalDate date);\n\n        public abstract void selectRange(LocalDate start, LocalDate end);\n\n        public abstract void selectUntil(LocalDate date);\n\n        public void clear() {\n            model.selectedDates.clear();\n            model.lastSelected = null;\n        }\n\n        public void clearAndSelect(LocalDate date) {\n            if (date != null) {\n                model.selectedDates.setAll(date);\n                model.lastSelected = date;\n            } else {\n                clear();\n            }\n        }\n\n        public void deselect(LocalDate date) {\n            model.selectedDates.remove(date);\n            if (model.lastSelected != null && model.lastSelected.equals(date)) {\n                model.lastSelected = null;\n            }\n        }\n\n        public static DateSelector createInstance(DateSelectionModel model) {\n            switch (model.getSelectionMode()) {\n                case SINGLE_DATE:\n                    return new SingleDateSelector(model);\n\n                case SINGLE_DATE_RANGE:\n                    return new SingleDateRangeSelector(model);\n\n                case MULTIPLE_DATES:\n                    return new MultipleDatesSelector(model);\n\n                default:\n                    throw new UnsupportedOperationException();\n            }\n        }\n    }\n\n    private static class SingleDateSelector extends DateSelector {\n\n        public SingleDateSelector(DateSelectionModel model) {\n            super(model);\n            if (model.selectedDates.size() > 1) {\n                if (model.lastSelected != null) {\n                    select(model.lastSelected);\n                } else {\n                    select(model.selectedDates.get(0));\n                }\n            }\n        }\n\n        @Override\n        public void select(LocalDate date) {\n            if (date != null) {\n                model.selectedDates.setAll(date);\n                model.lastSelected = date;\n            } else {\n                model.clear();\n            }\n        }\n\n        @Override\n        public void selectRange(LocalDate start, LocalDate end) {\n            if (start == null && end == null) {\n                model.clear();\n            } else if (end != null) {\n                select(end);\n            } else {\n                select(start);\n            }\n        }\n\n        @Override\n        public void selectUntil(LocalDate date) {\n            select(date);\n        }\n\n    }\n\n    private static final class SingleDateRangeSelector extends DateSelector {\n\n        private LocalDate rangeStart;\n        private LocalDate rangeEnd;\n\n        public SingleDateRangeSelector(DateSelectionModel model) {\n            super(model);\n\n            List<LocalDate> selection = new ArrayList<>();\n            selection.addAll(model.selectedDates);\n\n            if (!selection.isEmpty()) {\n                Collections.sort(selection);\n\n                boolean valid = true;\n                for (int i = 0; i < selection.size() - 1; i++) {\n                    if (i == selection.size() - 1) {\n                        break;\n                    }\n\n                    LocalDate pivot = selection.get(i);\n                    LocalDate next = selection.get(i + 1);\n\n                    if (!next.equals(pivot.plusDays(1))) {\n                        valid = false;\n                        break;\n                    }\n                }\n\n                if (!valid) {\n                    clear();\n                } else {\n                    rangeStart = selection.get(0);\n                    rangeEnd = selection.get(selection.size() - 1);\n                    model.lastSelected = rangeEnd;\n                }\n            }\n        }\n\n        @Override\n        public void select(LocalDate date) {\n            if (date != null) {\n                if (rangeStart == null) {\n                    model.selectedDates.setAll(date);\n                    model.lastSelected = date;\n                    rangeStart = date;\n                    rangeEnd = date;\n                } else {\n                    if (date.equals(rangeStart.plusDays(-1)) || date.equals(rangeEnd.plusDays(1))) {\n                        model.selectedDates.add(date);\n                        model.lastSelected = date;\n\n                        if (date.isAfter(rangeEnd)) {\n                            rangeEnd = date;\n                        }\n\n                        if (date.isBefore(rangeStart)) {\n                            rangeStart = date;\n                        }\n                    } else {\n                        rangeStart = null;\n                        rangeEnd = null;\n                        select(date);\n                    }\n                }\n            }\n        }\n\n        @Override\n        public void selectRange(LocalDate start, LocalDate end) {\n            if (start != null && end != null && !start.isAfter(end)) {\n                if (model.isEmpty() || isIntersecting(start, end)) {\n                    List<LocalDate> toSelect = new ArrayList<>();\n                    while (start.isBefore(end) || start.isEqual(end)) {\n                        if (!model.selectedDates.contains(start)) {\n                            toSelect.add(start);\n                        }\n                        start = start.plusDays(1);\n                    }\n\n                    if (!toSelect.isEmpty()) {\n                        LocalDate first = toSelect.get(0);\n                        LocalDate last = toSelect.get(toSelect.size() - 1);\n                        model.selectedDates.addAll(toSelect);\n                        model.lastSelected = last;\n\n                        if (rangeStart == null || rangeStart.isAfter(first)) {\n                            rangeStart = first;\n                        }\n\n                        if (rangeEnd == null || rangeEnd.isBefore(last)) {\n                            rangeEnd = last;\n                        }\n                    }\n                } else if (start.equals(rangeEnd.plusDays(1)) || end.equals(rangeStart.plusDays(-1))) {\n                    List<LocalDate> toSelect = new ArrayList<>();\n                    while (start.isBefore(end) || start.isEqual(end)) {\n                        toSelect.add(start);\n                        start = start.plusDays(1);\n                    }\n\n                    LocalDate first = toSelect.get(0);\n                    LocalDate last = toSelect.get(toSelect.size() - 1);\n                    model.selectedDates.addAll(toSelect);\n                    model.lastSelected = last;\n\n                    if (rangeStart == null || rangeStart.isAfter(first)) {\n                        rangeStart = first;\n                    }\n\n                    if (rangeEnd == null || rangeEnd.isBefore(last)) {\n                        rangeEnd = last;\n                    }\n                } else {\n                    List<LocalDate> toSelect = new ArrayList<>();\n                    while (start.isBefore(end) || start.isEqual(end)) {\n                        toSelect.add(start);\n                        start = start.plusDays(1);\n                    }\n\n                    LocalDate first = toSelect.get(0);\n                    LocalDate last = toSelect.get(toSelect.size() - 1);\n                    model.selectedDates.setAll(toSelect);\n                    model.lastSelected = last;\n\n                    if (rangeStart == null || rangeStart.isAfter(first)) {\n                        rangeStart = first;\n                    }\n\n                    if (rangeEnd == null || rangeEnd.isBefore(last)) {\n                        rangeEnd = last;\n                    }\n                }\n            }\n        }\n\n        @Override\n        public void selectUntil(LocalDate date) {\n            if (date != null && model.lastSelected != null) {\n                LocalDate start = model.lastSelected.isBefore(date) ? model.lastSelected : date;\n                LocalDate end = model.lastSelected.isBefore(date) ? date : model.lastSelected;\n                selectRange(start, end);\n                model.lastSelected = date;\n            }\n        }\n\n        @Override\n        public void clear() {\n            super.clear();\n            rangeStart = null;\n            rangeEnd = null;\n        }\n\n        private boolean isIntersecting(LocalDate start, LocalDate end) {\n            if ((start.isBefore(rangeStart) || start.isEqual(rangeStart)) && (end.isAfter(rangeEnd) || end.isEqual(rangeEnd))) {\n                return true;\n            }\n\n            if ((start.isEqual(rangeStart) || start.isAfter(rangeStart)) && (start.isBefore(rangeEnd) || start.isEqual(rangeEnd))) {\n                return true;\n            }\n\n            return (end.isEqual(rangeStart) || end.isAfter(rangeStart)) && (end.isBefore(rangeEnd) || end.isEqual(rangeEnd));\n\n        }\n\n        @Override\n        public void clearAndSelect(LocalDate date) {\n            super.clearAndSelect(date);\n            rangeStart = date;\n            rangeEnd = date;\n        }\n\n    }\n\n    private static final class MultipleDatesSelector extends DateSelector {\n\n        public MultipleDatesSelector(DateSelectionModel model) {\n            super(model);\n        }\n\n        @Override\n        public void select(LocalDate date) {\n            if (date != null) {\n                if (!model.selectedDates.contains(date)) {\n                    model.selectedDates.add(date);\n                }\n                model.lastSelected = date;\n            }\n        }\n\n        @Override\n        public void selectRange(LocalDate start, LocalDate end) {\n            if (start == null || end == null || start.isAfter(end)) {\n                return;\n            }\n\n            List<LocalDate> toSelect = new ArrayList<>();\n            while (start.isBefore(end) || start.equals(end)) {\n                if (!model.selectedDates.contains(start)) {\n                    toSelect.add(start);\n                }\n                start = start.plusDays(1);\n            }\n\n            model.selectedDates.addAll(toSelect);\n            model.lastSelected = toSelect.get(toSelect.size() - 1);\n        }\n\n        @Override\n        public void selectUntil(LocalDate date) {\n            if (date != null && model.lastSelected != null) {\n                LocalDate start = model.lastSelected.isBefore(date) ? model.lastSelected : date;\n                LocalDate end = model.lastSelected.isBefore(date) ? date : model.lastSelected;\n                selectRange(start, end);\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DayEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.DayEntryViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ReadOnlyMapWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableMap;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Skin;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A view representing an entry inside the {@link DayView} control. Instances of\n * this type are created by the {@link DayView} itelf via a pluggable factory.\n * The image below shows the default apperance of this view.\n *\n * <img src=\"doc-files/day-entry-view.png\" alt=\"Day Entry View\">\n *\n * @see DayView#entryViewFactoryProperty()\n */\npublic class DayEntryView extends EntryViewBase<DayView> {\n\n    /**\n     * Constructs a new entry view for the given calendar entry.\n     *\n     * @param entry the entry for which the view will be created\n     */\n    public DayEntryView(Entry<?> entry) {\n        super(entry);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new DayEntryViewSkin(this);\n    }\n\n    private final ReadOnlyMapWrapper<Pos, List<Node>> nodes = new ReadOnlyMapWrapper<>(this, \"nodes\");\n\n    /**\n     * A day entry view can be decorated with symbols / nodes. These nodes are stored\n     * in a hash map where the position of the nodes is the key.\n     *\n     * @return the map of nodes\n     */\n    public final ObservableMap<Pos, List<Node>> getNodes() {\n        return nodes.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the hashmap used to store nodes used for decorating the entry view.\n     *\n     * @return the nodes map\n     */\n    public final ReadOnlyMapWrapper<Pos, List<Node>> nodesProperty() {\n        return nodes;\n    }\n\n    /**\n     * Removes all nodes from all positions.\n     */\n    public void clearNodes() {\n        if (nodes.get() != null) {\n            nodes.get().clear();\n        }\n    }\n\n    /**\n     * Adds a node to the given position to the entry view.\n     *\n     * @param pos the position for the node\n     * @param node the node itself\n     */\n    public void addNode(Pos pos, Node node) {\n        if (nodes.get() == null) {\n             nodes.set(FXCollections.observableHashMap());\n        }\n\n        // force map invalidation event by completely replacing the list instead of just\n        // adding the new node to the existing list\n        final List<Node> nodes = this.nodes.computeIfAbsent(pos, p -> new ArrayList<>());\n        final List<Node> newNodes = new ArrayList<>(nodes);\n        newNodes.add(node);\n\n        this.nodes.put(pos, newNodes);\n    }\n\n    /**\n     * Removes the given node from the entry view.\n     *\n     * @param node the node to remove\n     */\n    public void removeNode(Node node) {\n        if (nodes.get() != null) {\n            nodes.values().forEach(nodesList -> nodesList.remove(node));\n        }\n    }\n\n    // MIN HEIGHT / TITLE HEIGHT\n\n    private final BooleanProperty minHeightEqualToTitleHeight = new SimpleBooleanProperty(this, \"minHeightEqualToTitleHeight\", true);\n\n    /**\n     * Controls whether the day entry view will at least always have the height of the title label.\n     *\n     * @return true if the entry has a minimum height that guarantees the visibility of the title label\n     */\n    public final BooleanProperty minHeightEqualToTitleHeightProperty() {\n        return minHeightEqualToTitleHeight;\n    }\n\n    public final boolean isMinHeightEqualToTitleHeight() {\n        return minHeightEqualToTitleHeight.get();\n    }\n\n    public final void setMinHeightEqualToTitleHeight(boolean minHeightEqualToTitleHeight) {\n        this.minHeightEqualToTitleHeight.set(minHeightEqualToTitleHeight);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.DayViewSkin;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\n\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A view representing a single day. This view can be customized to show all 24\n * hours at equal height, compress the early and late hours (unused hours), or\n * to hide these hours altogether.\n *\n * The view uses a factory to create instances of {@link DayEntryView}.\n * Applications can plug in their own factory to customize the appearance of\n * entry views.\n *\n * New calendar entries can be created by double clicking in the background of\n * the view.\n *\n * The image below shows an example of this view with three entry views and\n * compressed early hours.\n *\n * <img src=\"doc-files/day-view.png\" alt=\"Day View\">\n *\n */\npublic class DayView extends DayViewBase {\n\n\tprivate static final String DAY_VIEW = \"day-view\";\n\tprivate static final String DAY_VIEW_TODAY = \"today\";\n\tprivate static final String DAY_VIEW_WEEKEND_DAY = \"weekend-day\";\n\n\t/**\n\t * Constructs a new day view.\n\t */\n\tpublic DayView() {\n\t\tgetStyleClass().add(DAY_VIEW);\n\n\t\tInvalidationListener updateStyleClassListener = it -> updateStyleClasses();\n\n\t\tshowTodayProperty().addListener(updateStyleClassListener);\n\t\ttodayProperty().addListener(updateStyleClassListener);\n\t\tdateProperty().addListener(updateStyleClassListener);\n\t\tgetWeekendDays().addListener(updateStyleClassListener);\n\t\tupdateStyleClasses();\n\n\t\tselectionModeProperty().addListener(evt -> getSelections().clear());\n\n\t\tsetEntryViewFactory(DayEntryView::new);\n\t\tsetMinWidth(0); // important, so that multi day views apply same width for all day views\n\n\t\tnew DeleteHandler(this);\n\t}\n\n\t@Override\n\tprotected Skin<?> createDefaultSkin() {\n\t\treturn new DayViewSkin<>(this);\n\t}\n\n\tprivate void updateStyleClasses() {\n\t\tLocalDate date = getDate();\n\t\tif (date.equals(getToday()) && isShowToday()) {\n\t\t\tif (!getStyleClass().contains(DAY_VIEW_TODAY)) {\n\t\t\t\tgetStyleClass().add(DAY_VIEW_TODAY);\n\t\t\t}\n\t\t} else {\n\t\t\tgetStyleClass().remove(DAY_VIEW_TODAY);\n\t\t}\n\n\t\tif (getWeekendDays().contains(date.getDayOfWeek())) {\n\t\t\tif (!getStyleClass().contains(DAY_VIEW_WEEKEND_DAY)) {\n\t\t\t\tgetStyleClass().add(DAY_VIEW_WEEKEND_DAY);\n\t\t\t}\n\t\t} else {\n\t\t\tgetStyleClass().remove(DAY_VIEW_WEEKEND_DAY);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Optional<Calendar> getCalendarAt(double x, double y) {\n\t\tif (getLayout().equals(Layout.SWIMLANE)) {\n\t\t\tList<Calendar> visibleCalendars = getCalendars().filtered(this::isCalendarVisible);\n\t\t\tdouble calendarWidth = getWidth() / visibleCalendars.size();\n\t\t\tint index = (int) (x / calendarWidth);\n\t\t\tif (index < visibleCalendars.size()) {\n\t\t\t\treturn Optional.of(visibleCalendars.get(index));\n\t\t\t}\n\t\t}\n\n\t\treturn Optional.empty();\n\t}\n\n\tprivate final ObjectProperty<Callback<Entry<?>, DayEntryView>> entryViewFactory = new SimpleObjectProperty<>(this, \"entryViewFactory\");\n\n\t/**\n\t * A factory used for creating instances of {@link DayEntryView} for each\n\t * calendar entry that needs to be shown in this day view.\n\t *\n\t * @return the day entry view factory\n\t */\n\tpublic final ObjectProperty<Callback<Entry<?>, DayEntryView>> entryViewFactoryProperty() {\n\t\treturn entryViewFactory;\n\t}\n\n\t/**\n\t * Returns the value of {@link #entryViewFactoryProperty()}.\n\t *\n\t * @return the entry view factory\n\t */\n\tpublic final Callback<Entry<?>, DayEntryView> getEntryViewFactory() {\n\t\treturn entryViewFactoryProperty().get();\n\t}\n\n\t/**\n\t * Sets the value of {@link #entryViewFactoryProperty()}.\n\t *\n\t * @param factory\n\t *            the entry view factory\n\t */\n\tpublic final void setEntryViewFactory(Callback<Entry<?>, DayEntryView> factory) {\n\t\trequireNonNull(factory);\n\t\tentryViewFactoryProperty().set(factory);\n\t}\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DayViewBase.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.util.ViewHelper;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyBooleanProperty;\nimport javafx.beans.property.ReadOnlyBooleanWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.MapChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.Paint;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * The common superclass for all date controls that are used to display the 24\n * hours of a day: day view, week day view, timescale, week timescale, and\n * week view. Instances of this type can be configured to display hours at a\n * fixed height or alternatively a fixed number of hours for a given viewport\n * height (see {@link HoursLayoutStrategy}). This control also supports an early /\n * late hours concept where these hours can either be shown, or hidden, or shown\n * in a compressed way (see {@link EarlyLateHoursStrategy}). The idea behind\n * early / late hours is that often applications do not work with all 24 hours\n * of a day and hiding or compressing these hours allow the user to focus on\n * the relevant hours.\n */\npublic abstract class DayViewBase extends DateControl implements ZonedDateTimeProvider {\n\n    /**\n     * Constructs a new view.\n     */\n    public DayViewBase() {\n        MapChangeListener<? super Object, ? super Object> propertiesListener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(\"show.current.time.marker\")) {\n                    Boolean show = (Boolean) change.getValueAdded();\n                    showCurrentTimeMarker.set(show);\n                } else if (change.getKey().equals(\"show.current.time.today.marker\")) {\n                    Boolean show = (Boolean) change.getValueAdded();\n                    showCurrentTimeTodayMarker.set(show);\n                } else if (change.getKey().equals(\"earliest.time.used\")) {\n                    LocalTime time = (LocalTime) change.getValueAdded();\n                    earliestTimeUsed.set(time);\n                } else if (change.getKey().equals(\"latest.time.used\")) {\n                    LocalTime time = (LocalTime) change.getValueAdded();\n                    latestTimeUsed.set(time);\n                }\n            }\n        };\n\n        getProperties().addListener(propertiesListener);\n\n        final InvalidationListener trimListener = it -> {\n            if (isTrimTimeBounds()) {\n                trimTimeBounds();\n            }\n        };\n\n        earliestTimeUsedProperty().addListener(trimListener);\n        latestTimeUsedProperty().addListener(trimListener);\n        trimTimeBoundsProperty().addListener(trimListener);\n\n        installDefaultLassoFinishedBehaviour();\n    }\n\n    /**\n     * A list of possible grid types supported by the day view.\n     *\n     * @see #gridTypeProperty()\n     * @see #gridLinesProperty()\n     * @see #gridLineColorProperty()\n     */\n    public enum GridType {\n        STANDARD,\n        CUSTOM\n    }\n\n    private final ObjectProperty<GridType> gridType = new SimpleObjectProperty<>(this, \"gridType\", GridType.STANDARD);\n\n    public final GridType getGridType() {\n        return gridType.get();\n    }\n\n    /**\n     * Determines the type of grid / grid lines will be used for full hours,\n     * half hours, and so on. The {@link GridType#STANDARD} only supports grid lines\n     * for full hours and half hours. The {@link GridType#CUSTOM} can be configured\n     * via a {@link VirtualGrid} to show any kind of grid lines.\n     *\n     * @return the grid type\n     * @see #gridLinesProperty()\n     * @see #gridLineColorProperty()\n     */\n    public final ObjectProperty<GridType> gridTypeProperty() {\n        return gridType;\n    }\n\n    public final void setGridType(GridType gridType) {\n        this.gridType.set(gridType);\n    }\n\n    private final ObjectProperty<VirtualGrid> gridLines = new SimpleObjectProperty<>(this, \"virtualGrid\", new VirtualGrid(\"Grid Lines\", \"Grid\", ChronoUnit.MINUTES, 30));\n\n    public final VirtualGrid getGridLines() {\n        return gridLines.get();\n    }\n\n    /**\n     * A virtual grid used to control the placement of lightweight grid lines in the background\n     * of the view. These grid lines are drawn via the canvas API.\n     *\n     * @return the grid lines virtual grid object\n     */\n    public final ObjectProperty<VirtualGrid> gridLinesProperty() {\n        return gridLines;\n    }\n\n    public final void setGridLines(VirtualGrid gridLines) {\n        this.gridLines.set(gridLines);\n    }\n\n    private final ObjectProperty<Paint> gridLineColor = new SimpleObjectProperty<>(this, \"gridLineColor\", Color.LIGHTGRAY);\n\n    public final Paint getGridLineColor() {\n        return gridLineColor.get();\n    }\n\n    /**\n     * A color used to draw the lightweight grid lines in a background canvas.\n     *\n     * @return the grid line color\n     */\n    public final ObjectProperty<Paint> gridLineColorProperty() {\n        return gridLineColor;\n    }\n\n    public final void setGridLineColor(Paint gridLineColor) {\n        this.gridLineColor.set(gridLineColor);\n    }\n\n    /**\n     * A list of possible ways that entries can behave when the user switches\n     * to availability editing. The values determine if the entries should\n     * disappear, if they should become semi-transparent, or if they should stay\n     * completely visible.\n     */\n    public enum AvailabilityEditingEntryBehaviour {\n        /**\n         * Entry views will stay visible.\n         */\n        SHOW,\n\n        /**\n         * Entry views will be completely hidden.\n         */\n        HIDE,\n\n        /**\n         * Entry views will be semi-transparent.\n         */\n        OPACITY\n    }\n\n    private final ObjectProperty<AvailabilityEditingEntryBehaviour> entryViewAvailabilityEditingBehaviour = new SimpleObjectProperty<>(this, \"entryViewAvailabilityEditingBehaviour\", AvailabilityEditingEntryBehaviour.OPACITY);\n\n    public final AvailabilityEditingEntryBehaviour getEntryViewAvailabilityEditingBehaviour() {\n        return entryViewAvailabilityEditingBehaviour.get();\n    }\n\n    /**\n     * Determines how entry views should behave when the user switches to the availability editing\n     * mode. Entries can disappear, they can become semi-transparent, or the can stay completely\n     * visible.\n     *\n     * @return the behaviour of entry views when in availability editing mode\n     * @see DateControl#setEditAvailability(boolean)\n     */\n    public final ObjectProperty<AvailabilityEditingEntryBehaviour> entryViewAvailabilityEditingBehaviourProperty() {\n        return entryViewAvailabilityEditingBehaviour;\n    }\n\n    public final void setEntryViewAvailabilityEditingBehaviour(AvailabilityEditingEntryBehaviour behaviour) {\n        this.entryViewAvailabilityEditingBehaviour.set(behaviour);\n    }\n\n    private final DoubleProperty entryViewAvailabilityEditingOpacity = new SimpleDoubleProperty(this, \"entryViewAvailabilityEditingOpacity\", .25);\n\n    public final double getEntryViewAvailabilityEditingOpacity() {\n        return entryViewAvailabilityEditingOpacity.get();\n    }\n\n    /**\n     * A double value that determines how opaque entry views will be while the user is\n     * performing availability editing.\n     *\n     * @return the opacity value\n     */\n    public final DoubleProperty entryViewAvailabilityEditingOpacityProperty() {\n        return entryViewAvailabilityEditingOpacity;\n    }\n\n    public final void setEntryViewAvailabilityEditingOpacity(double entryViewAvailabilityEditingOpacity) {\n        this.entryViewAvailabilityEditingOpacity.set(entryViewAvailabilityEditingOpacity);\n    }\n\n    /**\n     * Registers a {@link #onLassoFinishedProperty()} that will add a\n     * calendar entry for the lasso selection into the current availability\n     * calendar.\n     *\n     * @see #availabilityCalendarProperty()\n     */\n    public void installDefaultLassoFinishedBehaviour() {\n        setOnLassoFinished((start, end) -> {\n            if (start == null || end == null) {\n                return;\n            }\n\n            if (isEditAvailability()) {\n                if (start.isAfter(end)) {\n                    Instant tmp = start;\n                    start = end;\n                    end = tmp;\n                }\n\n                Entry<?> entry = new Entry<>(\"Availability Entry\", new Interval(start, end, getZoneId()));\n                Calendar availabilityCalendar = getAvailabilityCalendar();\n                availabilityCalendar.addEntry(entry);\n            }\n        });\n    }\n\n    /**\n     * Returns the zoned start time of the day view for the view's current date,\n     * its start time, and its time zone.\n     *\n     * @return the zoned start time of the view\n     * @see #getDate()\n     * @see #getStartTime()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getZonedDateTimeStart() {\n        return ZonedDateTime.of(getDate(), getStartTime(), getZoneId());\n    }\n\n    /**\n     * Returns the zoned minimum time of the day view for the view's current date,\n     * for {@link LocalTime#MIN}, and its time zone.\n     *\n     * @return the zoned minimum time of the view\n     * @see #getDate()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getZonedDateTimeMin() {\n        return ZonedDateTime.of(getDate(), LocalTime.MIN, getZoneId());\n    }\n\n    /**\n     * Returns the zoned end time of the day view for the view's current date,\n     * its end time, and its time zone.\n     *\n     * @return the zoned end time of the view\n     * @see #getDate()\n     * @see #getEndTime() ()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getZonedDateTimeEnd() {\n        return ZonedDateTime.of(getDate(), getEndTime(), getZoneId());\n    }\n\n    /**\n     * Returns the zoned maximum time of the day view for the view's current date,\n     * for {@link LocalTime#MAX}, and its time zone.\n     *\n     * @return the zoned maximum time of the view\n     * @see #getDate()\n     * @see #getZoneId()\n     */\n    public final ZonedDateTime getZonedDateTimeMax() {\n        return ZonedDateTime.of(getDate(), LocalTime.MAX, getZoneId());\n    }\n\n    private final ObjectProperty<ZonedDateTime> scrollTime = new SimpleObjectProperty<>(this, \"scrollTime\", ZonedDateTime.of(LocalDate.now(), LocalTime.MIN, ZoneId.systemDefault()));\n\n    public final ZonedDateTime getScrollTime() {\n        return scrollTime.get();\n    }\n\n    public final ObjectProperty<ZonedDateTime> scrollTimeProperty() {\n        return scrollTime;\n    }\n\n    public final void setScrollTime(ZonedDateTime scrollTime) {\n        this.scrollTime.set(scrollTime);\n    }\n\n    private final BooleanProperty scrollingEnabled = new SimpleBooleanProperty(this, \"scrollingEnabled\", false);\n\n    public final boolean isScrollingEnabled() {\n        return scrollingEnabled.get();\n    }\n\n    public final BooleanProperty scrollingEnabledProperty() {\n        return scrollingEnabled;\n    }\n\n    public final void setScrollingEnabled(boolean scrollingEnabled) {\n        this.scrollingEnabled.set(scrollingEnabled);\n    }\n\n    private final DoubleProperty entryWidthPercentage = new SimpleDoubleProperty(this, \"entryWidthPercentage\", 100) {\n\n        @Override\n        public void set(double newValue) {\n            if (newValue < 10 || newValue > 100) {\n                throw new IllegalArgumentException(\"percentage width must be between 10 and 100 but was \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * A percentage value used to specify how much of the available width inside the\n     * view will be utilized by the entry views. The default value is 100%, however\n     * applications might want to set a smaller value to allow the user to click and\n     * create new entries in already used time intervals.\n     *\n     * @return the entry percentage width\n     */\n    public final DoubleProperty entryWidthPercentageProperty() {\n        return entryWidthPercentage;\n    }\n\n    /**\n     * Sets the value of {@link #entryWidthPercentage}.\n     *\n     * @param percentage the new percentage width\n     */\n    public final void setEntryWidthPercentage(double percentage) {\n        this.entryWidthPercentage.set(percentage);\n    }\n\n    /**\n     * Returns the value of {@link #entryWidthPercentageProperty()}.\n     *\n     * @return the percentage width\n     */\n    public double getEntryWidthPercentage() {\n        return entryWidthPercentage.get();\n    }\n\n    public static final double MILLIS_PER_HOUR = 3_600_000d;\n\n    /**\n     * Returns the time instant at the location of the given mouse event.\n     *\n     * @param evt the mouse event\n     * @return the time at the mouse event location\n     */\n    public Instant getInstantAt(MouseEvent evt) {\n        return getInstantAt(evt.getX(), evt.getY());\n    }\n\n    /**\n     * Returns the time instant at the given location.\n     *\n     * @param x the x-coordinate\n     * @param y the y-coordinate\n     * @return the time at the given location\n     */\n    public Instant getInstantAt(double x, double y) {\n        return getZonedDateTimeAt(x, y).toInstant();\n    }\n\n    /**\n     * Returns the zoned date time at the given location.\n     *\n     * @param x the x-coordinate\n     * @param y the y-coordinate\n     * @return the time at the given location\n     */\n    public ZonedDateTime getZonedDateTimeAt(double x, double y) {\n        return getZonedDateTimeAt(x, y, getZoneId());\n    }\n\n    @Override\n    public ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId) {\n        if (isScrollingEnabled()) {\n\n            final double mpp = MILLIS_PER_HOUR / getHourHeight();\n            final long millis = (long) (y * mpp);\n            return getScrollTime().plus(millis, ChronoUnit.MILLIS);\n\n        } else {\n\n            return ZonedDateTime.ofInstant(ViewHelper.getInstantAt(this, y), getZoneId());\n\n        }\n    }\n\n    /**\n     * Returns the zoned date time version of the given time for the view and\n     * its current date and time zone.\n     *\n     * @param time the local time to convert to a zoned date time\n     * @return the zoned date time version of the given time\n     */\n    public final ZonedDateTime getZonedDateTime(LocalTime time) {\n        return ZonedDateTime.of(getDate(), time, getZoneId());\n    }\n\n    /**\n     * Returns the y coordinate for the given time instance. Uses {@link #getZoneId()} to\n     * create a {@link ZonedDateTime} object on-the-fly.\n     *\n     * @param instant the time for which to return the coordinate\n     * @return the y coordinate\n     */\n    public final double getLocation(Instant instant) {\n        return ViewHelper.getTimeLocation(this, instant);\n    }\n\n    /**\n     * Returns the y coordinate for the given time. This method delegates\n     * to {@link ViewHelper#getTimeLocation(DayViewBase, ZonedDateTime)}.\n     *\n     * @param time the time for which to return the coordinate\n     * @return the y coordinate\n     * @throws UnsupportedOperationException if {@link #scrollingEnabled} is set to true\n     */\n    public final double getLocation(ZonedDateTime time) {\n        return ViewHelper.getTimeLocation(this, time);\n    }\n\n    /**\n     * An enum listing possible methods of determining overlapping entries. Calendar entries\n     * can either overlap because of their time intervals or because of their visual bounds.\n     *\n     * @see DayEntryView#alignmentStrategyProperty()\n     */\n    public enum OverlapResolutionStrategy {\n        TIME_BOUNDS,\n        VISUAL_BOUNDS,\n        OFF,\n    }\n\n    private final ObjectProperty<OverlapResolutionStrategy> overlapResolutionStrategy = new SimpleObjectProperty<>(this, \"overlapCriteria\", OverlapResolutionStrategy.TIME_BOUNDS);\n\n    /**\n     * A property used to instruct the view how to resolve overlapping\n     * entries, whether the resolution should be based on the time intervals of calendar\n     * entries or on their visual bounds or completely disabled. The default value is\n     * {@link OverlapResolutionStrategy#TIME_BOUNDS}.\n     *\n     * @return the overlap resolution strategy\n     */\n    public final ObjectProperty<OverlapResolutionStrategy> overlapResolutionStrategyProperty() {\n        return overlapResolutionStrategy;\n    }\n\n    public final OverlapResolutionStrategy getOverlapResolutionStrategy() {\n        return overlapResolutionStrategy.get();\n    }\n\n    public final void setOverlapResolutionStrategy(OverlapResolutionStrategy strategy) {\n        this.overlapResolutionStrategy.set(strategy);\n    }\n\n    /**\n     * An enumerator used for specifying how to deal with late and early hours.\n     *\n     * @see DayViewBase#earlyLateHoursStrategyProperty()\n     * @see DayViewBase#startTimeProperty()\n     * @see DayViewBase#endTimeProperty()\n     */\n    public enum EarlyLateHoursStrategy {\n\n        /**\n         * Show all hours with the same height.\n         */\n        SHOW,\n\n        /**\n         * Shows early / late hours with a compressed height and all other hours\n         * with the standard height.\n         *\n         * @see DayViewBase#setHourHeight(double)\n         * @see DayViewBase#setHourHeightCompressed(double)\n         */\n        SHOW_COMPRESSED,\n\n        /**\n         * Hide the early / late hours.\n         */\n        HIDE\n    }\n\n    private final ObjectProperty<EarlyLateHoursStrategy> earlyLateHoursStrategy = new SimpleObjectProperty<>(this, \"earlyLateHoursStrategy\", EarlyLateHoursStrategy.SHOW);\n\n    /**\n     * Specifies a strategy for dealing with early / late hours. The idea behind\n     * early / late hours is that often applications do not work with all 24\n     * hours of a day and hiding or compressing these hours allow the user to\n     * focus on the relevant hours.\n     *\n     * @return the early / late hours strategy\n     * @see #startTimeProperty()\n     * @see #endTimeProperty()\n     */\n    public final ObjectProperty<EarlyLateHoursStrategy> earlyLateHoursStrategyProperty() {\n        return earlyLateHoursStrategy;\n    }\n\n    /**\n     * Sets the value of {@link #earlyLateHoursStrategyProperty()}.\n     *\n     * @param strategy the strategy to use for early / late hours\n     */\n    public final void setEarlyLateHoursStrategy(EarlyLateHoursStrategy strategy) {\n        requireNonNull(strategy);\n        earlyLateHoursStrategy.set(strategy);\n    }\n\n    /**\n     * Returns the value of {@link #earlyLateHoursStrategyProperty()}.\n     *\n     * @return the early / late hours strategy\n     */\n    public final EarlyLateHoursStrategy getEarlyLateHoursStrategy() {\n        return earlyLateHoursStrategy.get();\n    }\n\n    /**\n     * An enumerator used for specifying how to lay out hours. The view can\n     * either show a fixed number of hours in the available viewport or each\n     * hour at a fixed height.\n     *\n     * @see DayViewBase#hoursLayoutStrategyProperty()\n     * @see DayViewBase#visibleHoursProperty()\n     */\n    public enum HoursLayoutStrategy {\n\n        /**\n         * Keeps the height of each hour constant.\n         */\n        FIXED_HOUR_HEIGHT,\n\n        /**\n         * Keeps the number of hours shown in the viewport of the scrollpane\n         * constant.\n         */\n        FIXED_HOUR_COUNT\n    }\n\n    private final ObjectProperty<HoursLayoutStrategy> hoursLayoutStrategy = new SimpleObjectProperty<>(this, \"hoursLayoutStrategy\", HoursLayoutStrategy.FIXED_HOUR_COUNT);\n\n    /**\n     * The layout strategy used by this view for showing hours. The view can\n     * either show a fixed number of hours in the available viewport or each\n     * hour at a fixed height.\n     *\n     * @return the hours layout strategy\n     * @see DayViewBase#visibleHoursProperty()\n     */\n    public final ObjectProperty<HoursLayoutStrategy> hoursLayoutStrategyProperty() {\n        return hoursLayoutStrategy;\n    }\n\n    /**\n     * Sets the value of {@link #hoursLayoutStrategyProperty()}.\n     *\n     * @param strategy the hours layout strategy\n     */\n    public final void setHoursLayoutStrategy(HoursLayoutStrategy strategy) {\n        requireNonNull(strategy);\n        hoursLayoutStrategy.set(strategy);\n    }\n\n    /**\n     * Returns the value of {@link #hoursLayoutStrategyProperty()}.\n     *\n     * @return the hours layout strategy\n     */\n    public final HoursLayoutStrategy getHoursLayoutStrategy() {\n        return hoursLayoutStrategy.get();\n    }\n\n    private final IntegerProperty visibleHours = new SimpleIntegerProperty(this, \"visibleHours\", 10);\n\n    /**\n     * The number of visible hours that the application wants to present to the\n     * user at any time.\n     *\n     * @return the number of visible hours\n     * @see #hoursLayoutStrategyProperty()\n     */\n    public final IntegerProperty visibleHoursProperty() {\n        return visibleHours;\n    }\n\n    /**\n     * Sets the value of {@link #visibleHoursProperty()}.\n     *\n     * @param hours the number of visible hours\n     */\n    public final void setVisibleHours(int hours) {\n        visibleHoursProperty().set(hours);\n    }\n\n    /**\n     * Returns the value of {@link #visibleHoursProperty()}.\n     *\n     * @return the number of visible hours\n     */\n    public final int getVisibleHours() {\n        return visibleHoursProperty().get();\n    }\n\n    private final DoubleProperty minHourHeight = new SimpleDoubleProperty(this, \"hourHeight\", 20);\n\n    public final double getMinHourHeight() {\n        return minHourHeight.get();\n    }\n\n    /**\n     * Returns the minimum height of an hour interval. Used for zoom in / out operations.\n     *\n     * @return the minimum hour height\n     * @see #hourHeightProperty()\n     */\n    public final DoubleProperty minHourHeightProperty() {\n        return minHourHeight;\n    }\n\n    public final void setMinHourHeight(double minHourHeight) {\n        this.minHourHeight.set(minHourHeight);\n    }\n\n    private final DoubleProperty maxHourHeight = new SimpleDoubleProperty(this, \"hourHeight\", 200);\n\n    public final double getMaxHourHeight() {\n        return maxHourHeight.get();\n    }\n\n    /**\n     * Returns the maximum height of an hour interval. Used for zoom in / out operations.\n     *\n     * @return the maximum hour height\n     * @see #hourHeightProperty()\n     */\n    public final DoubleProperty maxHourHeightProperty() {\n        return maxHourHeight;\n    }\n\n    public final void setMaxHourHeight(double maxHourHeight) {\n        this.maxHourHeight.set(maxHourHeight);\n    }\n\n    private final DoubleProperty hourHeight = new SimpleDoubleProperty(this, \"hourHeight\", 70);\n\n    /**\n     * The height used for each hour shown by the view.\n     *\n     * @return the hour height\n     * @see #hoursLayoutStrategyProperty()\n     */\n    public final DoubleProperty hourHeightProperty() {\n        return hourHeight;\n    }\n\n    /**\n     * Sets the value of {@link #hourHeightProperty()}.\n     *\n     * @param height the hour height\n     */\n    public final void setHourHeight(double height) {\n        if (height < 1) {\n            throw new IllegalArgumentException(\"height must be larger than 0 but was \" + height);\n        }\n        hourHeightProperty().set(height);\n    }\n\n    /**\n     * Returns the value of {@link #hourHeightProperty()}.\n     *\n     * @return the hour height\n     */\n    public final double getHourHeight() {\n        return hourHeightProperty().get();\n    }\n\n    private final DoubleProperty hourHeightCompressed = new SimpleDoubleProperty(this, \"hourHeightCompressed\", 10);\n\n    /**\n     * The height used for each early / late hour shown by the view when using\n     * the {@link EarlyLateHoursStrategy#SHOW_COMPRESSED} strategy.\n     *\n     * @return the compressed hour height\n     * @see #hoursLayoutStrategyProperty()\n     */\n    public final DoubleProperty hourHeightCompressedProperty() {\n        return hourHeightCompressed;\n    }\n\n    /**\n     * Sets the value of {@link #hourHeightCompressedProperty()}.\n     *\n     * @param height the compressed hour height\n     */\n    public final void setHourHeightCompressed(double height) {\n        if (height < 1) {\n            throw new IllegalArgumentException(\"height must be larger than 0 but was \" + height);\n        }\n        hourHeightCompressedProperty().set(height);\n    }\n\n    /**\n     * Returns the value of {@link #hourHeightCompressedProperty()}.\n     *\n     * @return the compressed hour height\n     */\n    public final double getHourHeightCompressed() {\n        return hourHeightCompressedProperty().get();\n    }\n\n    // Current time marker support.\n\n    private final ReadOnlyBooleanWrapper showCurrentTimeMarker = new ReadOnlyBooleanWrapper(this, \"showCurrentTimeMarker\", false);\n\n    /**\n     * A read-only property used to indicate whether the view should show the\n     * \"current time\" marker. Normally a day view will show this marker if the\n     * date shown by the view is equal to the value of \"today\". Depending on the\n     * subclass the marker can be a red label (see {@link TimeScaleView}) or a\n     * thin red line (see red time label and line in image below).\n     *\n     * <img src=\"doc-files/current-time-marker.png\" alt=\"Current Time Marker\">\n     *\n     * @return true if the current time marker should be shown\n     * @see #showCurrentTimeTodayMarkerProperty()\n     * @see #dateProperty()\n     * @see #todayProperty()\n     */\n    public final ReadOnlyBooleanProperty showCurrentTimeMarkerProperty() {\n        return showCurrentTimeMarker;\n    }\n\n    /**\n     * Returns the value of {@link #showCurrentTimeMarkerProperty()}.\n     *\n     * @return true if the view should display the marker\n     */\n    public final boolean isShowCurrentTimeMarker() {\n        return showCurrentTimeMarker.get();\n    }\n\n    private final ReadOnlyBooleanWrapper showCurrentTimeTodayMarker = new ReadOnlyBooleanWrapper(this, \"showCurrentTimeTodayMarker\", false);\n\n    /**\n     * A read-only property used to indicate whether the view should show the\n     * \"current time today\" marker. Normally a day view will show this marker if\n     * the date shown by the view is equal to the value of \"today\". The default\n     * marker is a red circle (see circle in the image below).\n     *\n     * <img src=\"doc-files/current-time-marker.png\" alt=\"Current Time Marker\">\n     *\n     * @return true if the current time marker should be shown\n     * @see #showCurrentTimeMarkerProperty()\n     * @see #dateProperty()\n     * @see #todayProperty()\n     */\n    public final ReadOnlyBooleanProperty showCurrentTimeTodayMarkerProperty() {\n        return showCurrentTimeTodayMarker;\n    }\n\n    /**\n     * Returns the value of {@link #showCurrentTimeTodayMarkerProperty()}.\n     *\n     * @return true if the view should show the marker\n     */\n    public final boolean isShowCurrentTimeTodayMarker() {\n        return showCurrentTimeTodayMarker.get();\n    }\n\n    private final BooleanProperty enableCurrentTimeCircle = new SimpleBooleanProperty(this, \"enableCurrentTimeCircle\", true);\n\n    /**\n     * A property used to signal whether the application wants to use the red (default) dot\n     * used for marking the current system time. The default value is true, so the dot will be\n     * shown.\n     *\n     * @return true if the current time will be marked with a red dot\n     */\n    public final BooleanProperty enableCurrentTimeCircleProperty() {\n        return enableCurrentTimeCircle;\n    }\n\n    /**\n     * Returns the value of {@link #enableCurrentTimeCircleProperty}.\n     *\n     * @return true if the current time will be marked with a red dot\n     */\n    public final boolean isEnableCurrentCircleMarker() {\n        return enableCurrentTimeCircle.get();\n    }\n\n    /**\n     * Sets the value of {@link #enableCurrentTimeCircleProperty}.\n     *\n     * @param enable if true the current time will be marked with a red dot\n     */\n    public final void setEnableCurrentTimeCircle(boolean enable) {\n        enableCurrentTimeCircle.set(enable);\n    }\n\n    private final BooleanProperty enableCurrentTimeMarker = new SimpleBooleanProperty(this, \"enableCurrentTimeMarker\", true);\n\n    /**\n     * A property used to signal whether the application wants to use the red (default) line\n     * used for marking the current system time. The default value is true, so the line will be\n     * shown.\n     *\n     * @return true if the current time will be marked with a red line / text\n     */\n    public final BooleanProperty enableCurrentTimeMarkerProperty() {\n        return enableCurrentTimeMarker;\n    }\n\n    /**\n     * Returns the value of {@link #enableCurrentTimeMarkerProperty}.\n     *\n     * @return true if the current time will be marked with a red line / text\n     */\n    public final boolean isEnableCurrentTimeMarker() {\n        return enableCurrentTimeMarker.get();\n    }\n\n    /**\n     * Sets the value of {@link #enableCurrentTimeMarkerProperty}.\n     *\n     * @param enable if true the current time will be marked with a red line / text\n     */\n    public final void setEnableCurrentTimeMarker(boolean enable) {\n        enableCurrentTimeMarker.set(enable);\n    }\n\n    private final ReadOnlyObjectWrapper<LocalTime> earliestTimeUsed = new ReadOnlyObjectWrapper<>(this, \"earliestTimeUsed\");\n\n    /**\n     * A read-only property used for informing the application about the earliest time point used\n     * by any of the calendar entries currently shown by the view.\n     *\n     * @return the earliest time used by the view\n     */\n    public final ReadOnlyObjectProperty<LocalTime> earliestTimeUsedProperty() {\n        return earliestTimeUsed.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #earliestTimeUsedProperty()}.\n     *\n     * @return the earliest time used\n     */\n    public final LocalTime getEarliestTimeUsed() {\n        return earliestTimeUsed.get();\n    }\n\n    private final ReadOnlyObjectWrapper<LocalTime> latestTimeUsed = new ReadOnlyObjectWrapper<>(this, \"latestTimeUsed\");\n\n    /**\n     * A read-only property used for informing the application about the latest time point used\n     * by any of the calendar entries currently shown by the view.\n     *\n     * @return the latest time used by the view\n     */\n    public final ReadOnlyObjectProperty<LocalTime> latestTimeUsedProperty() {\n        return latestTimeUsed.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #latestTimeUsedProperty()}.\n     *\n     * @return the latest time used\n     */\n    public final LocalTime getLatestTimeUsed() {\n        return latestTimeUsed.get();\n    }\n\n    private final BooleanProperty trimTimeBounds = new SimpleBooleanProperty(this, \"timTimeBounds\", false);\n\n    /**\n     * A property to control whether the view should automatically adjust the earliest and latest\n     * time used properties based on the currently showing entries.\n     *\n     * @return true if the time bounds will be automatically trimmed\n     * @see #earliestTimeUsedProperty()\n     * @see #latestTimeUsedProperty()\n     */\n    public final BooleanProperty trimTimeBoundsProperty() {\n        return trimTimeBounds;\n    }\n\n    public final boolean isTrimTimeBounds() {\n        return trimTimeBounds.get();\n    }\n\n    public final void setTrimTimeBounds(boolean trimTimeBounds) {\n        this.trimTimeBounds.set(trimTimeBounds);\n    }\n\n    private void trimTimeBounds() {\n        if (this instanceof WeekDayView) {\n            return;\n        }\n\n        LoggingDomain.PRINTING.fine(\"trimming hours\");\n\n        LocalTime st = LocalTime.of(8, 0);\n        LocalTime et = LocalTime.of(19, 0);\n\n        LocalTime etu = getEarliestTimeUsed();\n        LocalTime ltu = getLatestTimeUsed();\n\n        LoggingDomain.PRINTING.fine(\"earliest time: \" + etu + \", latest time: \" + ltu);\n\n        setEarlyLateHoursStrategy(EarlyLateHoursStrategy.HIDE);\n\n        if (etu != null && ltu != null && ltu.isAfter(etu)) {\n            // some padding before the first entry\n            if (!etu.isBefore(LocalTime.of(1, 0))) {\n                etu = etu.minusHours(1);\n            } else {\n                etu = LocalTime.MIN;\n            }\n\n            // some padding after the last entry\n            if (!ltu.isAfter(LocalTime.of(23, 0))) {\n                ltu = ltu.plusHours(1);\n            } else {\n                ltu = LocalTime.MAX;\n            }\n\n            // only adjust start time if it is too late\n            if (etu.isBefore(st.plusHours(1))) {\n                setStartTime(etu);\n            } else {\n                setStartTime(st);\n            }\n\n            // only adjust end time if it is too early\n            if (ltu.isAfter(et.minusHours(1))) {\n                setEndTime(ltu);\n            } else {\n                setEndTime(et);\n            }\n        } else {\n            setStartTime(st);\n            setEndTime(et);\n        }\n\n        setVisibleHours(Math.min(24, (int) getStartTime().until(getEndTime(), ChronoUnit.HOURS)));\n    }\n\n    private final ObjectProperty<BiConsumer<Instant, Instant>> onLassoFinished = new SimpleObjectProperty<>(this, \"onLassoFinished\", (start, end) -> {});\n\n    public final BiConsumer<Instant, Instant> getOnLassoFinished() {\n        return onLassoFinished.get();\n    }\n\n    public final ObjectProperty<BiConsumer<Instant, Instant>> onLassoFinishedProperty() {\n        return onLassoFinished;\n    }\n\n    public final void setOnLassoFinished(BiConsumer<Instant, Instant> onLassoFinished) {\n        this.onLassoFinished.set(onLassoFinished);\n    }\n\n    private final ObjectProperty<Paint> lassoColor = new SimpleObjectProperty<>(this, \"lassoColor\", Color.GREY);\n\n    public final Paint getLassoColor() {\n        return lassoColor.get();\n    }\n\n    public final ObjectProperty<Paint> lassoColorProperty() {\n        return lassoColor;\n    }\n\n    public final void setLassoColor(Paint lassoColor) {\n        this.lassoColor.set(lassoColor);\n    }\n\n    private final ObjectProperty<Instant> lassoStart = new SimpleObjectProperty<>(this, \"lassoStart\");\n\n    public final Instant getLassoStart() {\n        return lassoStart.get();\n    }\n\n    public final ObjectProperty<Instant> lassoStartProperty() {\n        return lassoStart;\n    }\n\n    public final void setLassoStart(Instant lassoStart) {\n        this.lassoStart.set(lassoStart);\n    }\n\n    private final ObjectProperty<Instant> lassoEnd = new SimpleObjectProperty<>(this, \"lassoEnd\");\n\n    public final Instant getLassoEnd() {\n        return lassoEnd.get();\n    }\n\n    public final ObjectProperty<Instant> lassoEndProperty() {\n        return lassoEnd;\n    }\n\n    public final void setLassoEnd(Instant lassoEnd) {\n        this.lassoEnd.set(lassoEnd);\n    }\n\n    private final BooleanProperty enableStartAndEndTimesFlip = new SimpleBooleanProperty(this, \"enableStartAndEndTimesFlip\", false);\n\n    public final boolean isEnableStartAndEndTimesFlip() {\n        return enableStartAndEndTimesFlip.get();\n    }\n\n    /**\n     * Determines whether the user can flip the start and the end time of an entry by\n     * dragging either one to the other side of the other one.\n     *\n     * @return whether start and end times can be flipped\n     */\n    public final BooleanProperty enableStartAndEndTimesFlipProperty() {\n        return enableStartAndEndTimesFlip;\n    }\n\n    public final void setEnableStartAndEndTimesFlip(boolean enableStartAndEndTimesFlip) {\n        this.enableStartAndEndTimesFlip.set(enableStartAndEndTimesFlip);\n    }\n\n    /**\n     * Invokes {@link DateControl#bind(DateControl, boolean)} and adds some more\n     * bindings between this control and the given control.\n     *\n     * @param otherControl the control that will be bound to this control\n     * @param bindDate     if true will also bind the date property\n     */\n    public final void bind(DayViewBase otherControl, boolean bindDate) {\n        super.bind(otherControl, bindDate);\n\n        Bindings.bindBidirectional(otherControl.earlyLateHoursStrategyProperty(), earlyLateHoursStrategyProperty());\n        Bindings.bindBidirectional(otherControl.hoursLayoutStrategyProperty(), hoursLayoutStrategyProperty());\n        Bindings.bindBidirectional(otherControl.hourHeightProperty(), hourHeightProperty());\n        Bindings.bindBidirectional(otherControl.hourHeightCompressedProperty(), hourHeightCompressedProperty());\n        Bindings.bindBidirectional(otherControl.visibleHoursProperty(), visibleHoursProperty());\n        Bindings.bindBidirectional(otherControl.enableCurrentTimeMarkerProperty(), enableCurrentTimeMarkerProperty());\n        Bindings.bindBidirectional(otherControl.enableCurrentTimeCircleProperty(), enableCurrentTimeCircleProperty());\n        Bindings.bindBidirectional(otherControl.trimTimeBoundsProperty(), trimTimeBoundsProperty());\n        Bindings.bindBidirectional(otherControl.scrollingEnabledProperty(), scrollingEnabledProperty());\n        Bindings.bindBidirectional(otherControl.scrollTimeProperty(), scrollTimeProperty());\n        Bindings.bindBidirectional(otherControl.overlapResolutionStrategyProperty(), overlapResolutionStrategyProperty());\n        Bindings.bindBidirectional(otherControl.lassoStartProperty(), lassoStartProperty());\n        Bindings.bindBidirectional(otherControl.lassoEndProperty(), lassoEndProperty());\n        Bindings.bindBidirectional(otherControl.onLassoFinishedProperty(), onLassoFinishedProperty());\n        Bindings.bindBidirectional(otherControl.startTimeProperty(), startTimeProperty());\n        Bindings.bindBidirectional(otherControl.endTimeProperty(), endTimeProperty());\n        Bindings.bindBidirectional(otherControl.entryViewAvailabilityEditingBehaviourProperty(), entryViewAvailabilityEditingBehaviourProperty());\n        Bindings.bindBidirectional(otherControl.entryViewAvailabilityEditingOpacityProperty(), entryViewAvailabilityEditingOpacityProperty());\n        Bindings.bindBidirectional(otherControl.gridLinesProperty(), gridLinesProperty());\n        Bindings.bindBidirectional(otherControl.gridLineColorProperty(), gridLineColorProperty());\n        Bindings.bindBidirectional(otherControl.gridTypeProperty(), gridTypeProperty());\n        Bindings.bindBidirectional(otherControl.enableStartAndEndTimesFlipProperty(), enableStartAndEndTimesFlipProperty());\n    }\n\n    public final void unbind(DayViewBase otherControl) {\n        super.unbind(otherControl);\n\n        Bindings.unbindBidirectional(otherControl.earlyLateHoursStrategyProperty(), earlyLateHoursStrategyProperty());\n        Bindings.unbindBidirectional(otherControl.hoursLayoutStrategyProperty(), hoursLayoutStrategyProperty());\n        Bindings.unbindBidirectional(otherControl.hourHeightProperty(), hourHeightProperty());\n        Bindings.unbindBidirectional(otherControl.hourHeightCompressedProperty(), hourHeightCompressedProperty());\n        Bindings.unbindBidirectional(otherControl.visibleHoursProperty(), visibleHoursProperty());\n        Bindings.unbindBidirectional(otherControl.enableCurrentTimeMarkerProperty(), enableCurrentTimeMarkerProperty());\n        Bindings.unbindBidirectional(otherControl.enableCurrentTimeCircleProperty(), enableCurrentTimeCircleProperty());\n        Bindings.unbindBidirectional(otherControl.trimTimeBoundsProperty(), trimTimeBoundsProperty());\n        Bindings.unbindBidirectional(otherControl.scrollingEnabledProperty(), scrollingEnabledProperty());\n        Bindings.unbindBidirectional(otherControl.scrollTimeProperty(), scrollTimeProperty());\n        Bindings.unbindBidirectional(otherControl.overlapResolutionStrategyProperty(), overlapResolutionStrategyProperty());\n        Bindings.unbindBidirectional(otherControl.lassoStartProperty(), lassoStartProperty());\n        Bindings.unbindBidirectional(otherControl.lassoEndProperty(), lassoEndProperty());\n        Bindings.unbindBidirectional(otherControl.onLassoFinishedProperty(), onLassoFinishedProperty());\n        Bindings.unbindBidirectional(otherControl.startTimeProperty(), startTimeProperty());\n        Bindings.unbindBidirectional(otherControl.endTimeProperty(), endTimeProperty());\n        Bindings.unbindBidirectional(otherControl.entryViewAvailabilityEditingBehaviourProperty(), entryViewAvailabilityEditingBehaviourProperty());\n        Bindings.unbindBidirectional(otherControl.entryViewAvailabilityEditingOpacityProperty(), entryViewAvailabilityEditingOpacityProperty());\n        Bindings.unbindBidirectional(otherControl.gridLinesProperty(), gridLinesProperty());\n        Bindings.unbindBidirectional(otherControl.gridLineColorProperty(), gridLineColorProperty());\n        Bindings.unbindBidirectional(otherControl.gridTypeProperty(), gridTypeProperty());\n        Bindings.unbindBidirectional(otherControl.enableStartAndEndTimesFlipProperty(), enableStartAndEndTimesFlipProperty());\n    }\n\n    private static final String DAY_VIEW_BASE_CATEGORY = \"Date View Base\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(scrollingEnabledProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setScrollingEnabled((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isScrollingEnabled();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Enable Scrolling\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Support scrolling to previous or next days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(enableCurrentTimeMarkerProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEnableCurrentTimeMarker((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEnableCurrentTimeMarker();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Current time marker\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show current time marker.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(enableCurrentTimeCircleProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEnableCurrentTimeCircle((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEnableCurrentCircleMarker();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Current time circle\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show current time circle.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(earlyLateHoursStrategyProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEarlyLateHoursStrategy((EarlyLateHoursStrategy) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getEarlyLateHoursStrategy();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return EarlyLateHoursStrategy.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Early / Late Hours\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Early / Late Hours Layout Strategy\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(visibleHoursProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setVisibleHours((int) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getVisibleHours();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Visible Hours\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Number of visible hours\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(hourHeightProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setHourHeight((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getHourHeight();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Hour Height\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Height of one hour\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(hourHeightCompressedProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setHourHeightCompressed((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getHourHeightCompressed();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Hour Height Compressed\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Height of one hour when shown compressed (early / late hours).\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(hoursLayoutStrategyProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setHoursLayoutStrategy((HoursLayoutStrategy) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getHoursLayoutStrategy();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return HoursLayoutStrategy.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Layout Strategy\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Layout Strategy\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(trimTimeBoundsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setTrimTimeBounds((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isTrimTimeBounds();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Trim Time Bounds\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Adjust earliest / latest times shown\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(earliestTimeUsedProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n            }\n\n            @Override\n            public Object getValue() {\n                return getEarliestTimeUsed();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Earliest Time Used\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Earliest start time of any entry view\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(latestTimeUsedProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n            }\n\n            @Override\n            public Object getValue() {\n                return getLatestTimeUsed();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Latest Time Used\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Latest end time of any entry view\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(overlapResolutionStrategyProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setOverlapResolutionStrategy((OverlapResolutionStrategy) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getOverlapResolutionStrategy();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return OverlapResolutionStrategy.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Overlap Resolution\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Controls when entries are considered overlapping\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(entryViewAvailabilityEditingBehaviourProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEntryViewAvailabilityEditingBehaviour((AvailabilityEditingEntryBehaviour) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getEntryViewAvailabilityEditingBehaviour();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return AvailabilityEditingEntryBehaviour.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Availability Editing Entry Behaviour\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Determines how an entry view will be shown during availability editing.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(gridLinesProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setGridLines((VirtualGrid) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getGridLines();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return VirtualGrid.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Grid used for drawing grid lines\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Specifies the grid size.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(gridLineColorProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setGridLineColor((Paint) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getGridLineColor();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Paint.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Color used for drawing grid lines\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Specifies the grid line color.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(gridTypeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setGridType((GridType) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getGridType();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return GridType.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Grid Type\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Specifies the grid type.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(entryViewAvailabilityEditingOpacityProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setEntryViewAvailabilityEditingOpacity((double) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getEntryViewAvailabilityEditingOpacity();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Double.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Availability Editing Entry Opacity\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Determines how opaque an entry view will be shown during availability editing.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_VIEW_BASE_CATEGORY;\n            }\n\n            @Override\n            public boolean isEditable() {\n                return true;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DeleteHandler.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport javafx.scene.input.KeyEvent;\n\nimport static java.util.Objects.requireNonNull;\n\nclass DeleteHandler {\n\n    protected final DateControl dateControl;\n\n    public DeleteHandler(DateControl control) {\n        this.dateControl = requireNonNull(control);\n        dateControl.addEventHandler(KeyEvent.KEY_PRESSED, this::deleteEntries);\n    }\n\n    private void deleteEntries(KeyEvent evt) {\n        switch (evt.getCode()) {\n            case DELETE:\n            case BACK_SPACE:\n                for (Entry<?> entry : dateControl.getSelections()) {\n                    if (!dateControl.getEntryEditPolicy().call(new DateControl.EntryEditParameter(dateControl, entry, DateControl.EditOperation.DELETE))) {\n                        continue;\n                    }\n                    if (entry.isRecurrence()) {\n                        entry = entry.getRecurrenceSourceEntry();\n                    }\n                    if (!dateControl.getEntryEditPolicy().call(new DateControl.EntryEditParameter(dateControl, entry, DateControl.EditOperation.DELETE))) {\n                        continue;\n                    }\n\n                    Calendar calendar = entry.getCalendar();\n                    if (calendar != null && !calendar.isReadOnly()) {\n                        entry.removeFromCalendar();\n                    }\n                }\n                dateControl.clearSelection();\n                break;\n            case F5:\n                dateControl.refreshData();\n            default:\n                break;\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DetailedDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.view.AgendaView.AgendaEntryCell;\nimport impl.com.calendarfx.view.DetailedDayViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.util.Optional;\n\n/**\n * The detailed day view is a composite control consisting of a {@link DayView},\n * an {@link AllDayView}, an {@link CalendarHeaderView}, and a\n * {@link TimeScaleView}. The image below shows the standard appearance of the\n * view. The second image shows the same view with the optional agenda view made\n * visible.\n *\n * <img src=\"doc-files/detailed-day-view.png\" alt=\"Detailed Day View\">\n * <img src=\"doc-files/detailed-day-view-agenda.png\" alt=\"Detailed Day View Agenda\">\n *\n */\npublic class DetailedDayView extends DayViewBase {\n\n    private static final String DEFAULT_STYLE = \"detailed-day-view\";\n\n    private final AllDayView allDayView;\n    private final DayView dayView;\n    private final TimeScaleView timeScaleView;\n    private final CalendarHeaderView calendarHeader;\n    private final AgendaView agendaView;\n\n    /**\n     * Constructs a new day view.\n     */\n    public DetailedDayView() {\n        allDayView = new AllDayView(1);\n        dayView = new DayView();\n        timeScaleView = new TimeScaleView();\n        calendarHeader = new CalendarHeaderView();\n\n        agendaView = new AgendaView();\n        agendaView.setLookBackPeriodInDays(0);\n        agendaView.setLookAheadPeriodInDays(30);\n        agendaView.setShowStatusLabel(false);\n        agendaView.setCellFactory(view -> new AgendaEntryCell(view, false));\n\n        bind(dayView, true);\n        bind(timeScaleView, true);\n        bind(allDayView, true);\n        bind(agendaView, true);\n\n        calendarHeader.bind(this);\n\n        getStyleClass().add(DEFAULT_STYLE);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new DetailedDayViewSkin(this);\n    }\n\n    /**\n     * Returns the all day view sub control.\n     * \n     * @return the all day child node\n     */\n    public final AllDayView getAllDayView() {\n        return allDayView;\n    }\n\n    /**\n     * Returns the day view sub control.\n     * \n     * @return the day view child node\n     */\n    public final DayView getDayView() {\n        return dayView;\n    }\n\n    /**\n     * Returns the time scale sub control.\n     * \n     * @return the time scale child node\n     */\n    public final TimeScaleView getTimeScaleView() {\n        return timeScaleView;\n    }\n\n    /**\n     * Returns the calendar header sub control.\n     * \n     * @return the calendar header child node\n     */\n    public final CalendarHeaderView getCalendarHeaderView() {\n        return calendarHeader;\n    }\n\n    /**\n     * Returns the agenda view sub control.\n     * \n     * @return the agenda view child node\n     */\n    public final AgendaView getAgendaView() {\n        return agendaView;\n    }\n\n    // show all day view support\n\n    private final BooleanProperty showAllDayView = new SimpleBooleanProperty(\n            this, \"showAllDayView\", true);\n\n    /**\n     * A property used to toggle the visibility of the all day view.\n     *\n     * @return true if the all day view will be visible\n     */\n    public final BooleanProperty showAllDayViewProperty() {\n        return showAllDayView;\n    }\n\n    /**\n     * Sets the value of {@link #showAllDayViewProperty()}.\n     *\n     * @return true if the all day view will be visible\n     */\n    public final boolean isShowAllDayView() {\n        return showAllDayViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showAllDayViewProperty()}.\n     *\n     * @param show\n     *            true if the all day view will be visible\n     */\n    public final void setShowAllDayView(boolean show) {\n        showAllDayViewProperty().set(show);\n    }\n\n    // show agenda view support\n\n    private final BooleanProperty showAgendaView = new SimpleBooleanProperty(\n            this, \"showAgendaView\", false);\n\n    /**\n     * A property used to toggle the visibility of the agenda view.\n     *\n     * @return true if the agenda view will be visible\n     */\n    public final BooleanProperty showAgendaViewProperty() {\n        return showAgendaView;\n    }\n\n    /**\n     * Sets the value of {@link #showAgendaViewProperty()}.\n     *\n     * @return true if the agenda view will be visible\n     */\n    public final boolean isShowAgendaView() {\n        return showAgendaViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showAgendaViewProperty()}.\n     *\n     * @param show\n     *            if true the agenda view will be visible\n     */\n    public final void setShowAgendaView(boolean show) {\n        showAgendaViewProperty().set(show);\n    }\n\n    // show time scale view support\n\n    private final BooleanProperty showTimeScaleView = new SimpleBooleanProperty(\n            this, \"showTimeScaleView\", true);\n\n    /**\n     * A property used to toggle the visibility of the time scale view.\n     *\n     * @return true if the time scale view will be visible\n     */\n    public final BooleanProperty showTimeScaleViewProperty() {\n        return showTimeScaleView;\n    }\n\n    /**\n     * Returns the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @return true if the time scale view will be visible\n     */\n    public final boolean isShowTimeScaleView() {\n        return showTimeScaleViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @param show\n     *            if true the time scale view will be visible\n     */\n    public final void setShowTimeScaleView(boolean show) {\n        showTimeScaleViewProperty().set(show);\n    }\n\n    // show scrollbar support\n\n    private final BooleanProperty showScrollBar = new SimpleBooleanProperty(\n            this, \"showScrollBar\", true);\n\n    /**\n     * A property used to control the visibility of the vertial scrollbar.\n     *\n     * @return true if the scrollbar should be shown to the user\n     */\n    public final BooleanProperty showScrollBarProperty() {\n        return showScrollBar;\n    }\n\n    /**\n     * Sets the value of {@link #showScrollBarProperty()}.\n     *\n     * @param showScrollBar\n     *            if true the scrollbar will be visible\n     */\n    public final void setShowScrollBar(boolean showScrollBar) {\n        this.showScrollBar.set(showScrollBar);\n    }\n\n    /**\n     * Returns the value of {@link #showScrollBarProperty()}.\n     *\n     * @return true if the scrollbar will be visible\n     */\n    public final boolean isShowScrollBar() {\n        return showScrollBar.get();\n    }\n\n    private static final String DETAILED_DAY_VIEW_CATEGORY = \"Detailed Day View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowAllDayView((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowAllDayView();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showAllDayViewProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Show All Day View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show All Day View\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DETAILED_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowTimeScaleView((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowTimeScaleView();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTimeScaleViewProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Time Scale View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Time Scale View\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DETAILED_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowAgendaView((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowAgendaView();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showAgendaViewProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Agenda View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Agenda View\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DETAILED_DAY_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return DETAILED_DAY_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show ScrollBar\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show ScrollBar\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowScrollBar();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowScrollBar((boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showScrollBarProperty());\n            }\n        });\n\n        return items;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DetailedWeekView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport impl.com.calendarfx.view.DetailedWeekViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.util.Optional;\n\n/**\n * A view for showing several week days in a row, normally seven. However the\n * view can be configured to show any number of days. The view consists of several\n * larger sub controls: a week view, an all day view, a calendar header view, a\n * week day header view, and a time scale view. The image below shows the appearance\n * of this view.\n *\n * <img src=\"doc-files/detailed-week-view.png\" alt=\"Detailed Week View\">\n *\n * @see WeekView\n */\npublic class DetailedWeekView extends DayViewBase {\n\n    private static final String DEFAULT_STYLE_CLASS = \"detailed-week-view\";\n\n    private final WeekDayHeaderView weekDayHeaderView;\n\n    private final WeekView weekView;\n\n    private final AllDayView allDayView;\n\n    private final WeekTimeScaleView timeScaleView;\n\n    private final CalendarHeaderView calendarHeaderView;\n\n    /**\n     * Constructs a new view with seven days.\n     */\n    public DetailedWeekView() {\n        this(7);\n    }\n\n    /**\n     * Constructs a new view with the given number of days.\n     *\n     * @param numberOfDays the number of days to show in the view\n     */\n    public DetailedWeekView(int numberOfDays) {\n        setNumberOfDays(numberOfDays);\n\n\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        calendarHeaderView = new CalendarHeaderView();\n        calendarHeaderView.numberOfDaysProperty().bindBidirectional(numberOfDaysProperty());\n        calendarHeaderView.bind(this);\n\n        weekDayHeaderView = new WeekDayHeaderView();\n        weekDayHeaderView.showTodayProperty().bindBidirectional(showTodayProperty());\n\n        bind(weekDayHeaderView, true);\n        Bindings.bindBidirectional(weekDayHeaderView.numberOfDaysProperty(), numberOfDaysProperty());\n        Bindings.bindBidirectional(weekDayHeaderView.adjustToFirstDayOfWeekProperty(), adjustToFirstDayOfWeekProperty());\n\n        allDayView = new AllDayView(getNumberOfDays());\n\n        bind(allDayView, true);\n        Bindings.bindBidirectional(allDayView.numberOfDaysProperty(), numberOfDaysProperty());\n        Bindings.bindBidirectional(allDayView.adjustToFirstDayOfWeekProperty(), adjustToFirstDayOfWeekProperty());\n\n        weekView = new WeekView();\n        bind(weekView, true);\n        Bindings.bindBidirectional(weekView.numberOfDaysProperty(), numberOfDaysProperty());\n        Bindings.bindBidirectional(weekView.adjustToFirstDayOfWeekProperty(), adjustToFirstDayOfWeekProperty());\n\n        timeScaleView = new WeekTimeScaleView();\n        bind(timeScaleView, true);\n\n        startDate.bind(weekView.startDateProperty());\n        endDate.bind(weekView.endDateProperty());\n\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new DetailedWeekViewSkin(this);\n    }\n\n    /**\n     * Returns the header view control that gets shown when the layout of the\n     * control is set to {@link com.calendarfx.view.DateControl.Layout#SWIMLANE}\n     * . The header view displays the short name of each calendar that is\n     * currently visible.\n     *\n     * @return the calendar header view control\n     * @see Calendar#getShortName()\n     */\n    public final CalendarHeaderView getCalendarHeaderView() {\n        return calendarHeaderView;\n    }\n\n    /**\n     * Returns the header view that is used to display the names of the\n     * week days (Mon, Tue, Wed, ...).\n     *\n     * @return the week day header view\n     */\n    public final WeekDayHeaderView getWeekDayHeaderView() {\n        return weekDayHeaderView;\n    }\n\n    /**\n     * Returns the all day view child control.\n     *\n     * @return the all day view\n     */\n    public final AllDayView getAllDayView() {\n        return allDayView;\n    }\n\n    /**\n     * Returns the time scale child control.\n     *\n     * @return the time scale view\n     */\n    public final WeekTimeScaleView getTimeScaleView() {\n        return timeScaleView;\n    }\n\n    /**\n     * Returns the week view child control.\n     *\n     * @return the week view\n     */\n    public final WeekView getWeekView() {\n        return weekView;\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\", 7);\n\n    /**\n     * Stores the number of days that will be shown by this view. This value\n     * will be 1 if the view is used in combination with the {@link DayView} and\n     * 7 if used together with the {@link DetailedWeekView}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \"\n                    + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n\n    private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, \"adjustToFirstDayOfWeek\", true);\n\n    /**\n     * A flag used to indicate that the view should always show the first day of\n     * the week (e.g. \"Monday\") at its beginning even if the\n     * {@link #dateProperty()} is set to another day (e.g. \"Thursday\").\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final BooleanProperty adjustToFirstDayOfWeekProperty() {\n        return adjustToFirstDayOfWeek;\n    }\n\n    /**\n     * Returns the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final boolean isAdjustToFirstDayOfWeek() {\n        return adjustToFirstDayOfWeekProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @param adjust if true the view will always show the first day of the week\n     */\n    public final void setAdjustToFirstDayOfWeek(boolean adjust) {\n        adjustToFirstDayOfWeekProperty().set(adjust);\n    }\n\n    // all day view support\n\n    private final BooleanProperty showAllDayView = new SimpleBooleanProperty(this, \"showAllDayView\", true);\n\n    /**\n     * A property used to control the visibility of the all day view. The\n     * all day view displays those calendar entries that do not have a specific\n     * start and end time but that are valid for the entire day or day range.\n     *\n     * @return true if the all day view will be shown to the user\n     */\n    public final BooleanProperty showAllDayViewProperty() {\n        return showAllDayView;\n    }\n\n    /**\n     * Returns the value of {@link #showAllDayViewProperty()}.\n     *\n     * @return true if the all day view will be visible\n     */\n    public final boolean isShowAllDayView() {\n        return showAllDayViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showAllDayViewProperty()}.\n     *\n     * @param show if true the all day view will be visible\n     */\n    public final void setShowAllDayView(boolean show) {\n        showAllDayViewProperty().set(show);\n    }\n\n    // timescale support\n\n    private final BooleanProperty showTimeScaleView = new SimpleBooleanProperty(this, \"showTimeScaleView\", true);\n\n    /**\n     * A property used to control the visibility of the time scale on the left-hand side.\n     * The timescale displays the time of day.\n     *\n     * @return true if the scale will be visible\n     */\n    public final BooleanProperty showTimeScaleViewProperty() {\n        return showTimeScaleView;\n    }\n\n    /**\n     * Returns the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @return true if the time scale will be shown to the user\n     */\n    public final boolean isShowTimeScaleView() {\n        return showTimeScaleViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @param show if true the time scale will be shown to the user\n     */\n    public final void setShowTimeScaleView(boolean show) {\n        showTimeScaleViewProperty().set(show);\n    }\n\n    // week day view support\n\n    private final BooleanProperty showWeekDayHeaderView = new SimpleBooleanProperty(this, \"showWeekDayHeaderView\", true);\n\n    /**\n     * A property used to control the visibility of the week day header. The\n     * header displays the weekday names (Mo., Tu., ....).\n     *\n     * @return true if the week day header should be visible\n     */\n    public final BooleanProperty showWeekDayHeaderViewProperty() {\n        return showWeekDayHeaderView;\n    }\n\n    /**\n     * Returns the value of {@link #showWeekDayHeaderViewProperty()}.\n     *\n     * @return true if the week day header should be visible\n     */\n    public final boolean isShowWeekDayHeaderView() {\n        return showWeekDayHeaderViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showWeekDayHeaderViewProperty()}.\n     *\n     * @param show if true the week day header will be visible\n     */\n    public final void setShowWeekDayHeaderView(boolean show) {\n        showWeekDayHeaderViewProperty().set(show);\n    }\n\n    // show scrollbar support\n\n    private final BooleanProperty showScrollBar = new SimpleBooleanProperty(this, \"showScrollBar\", true);\n\n    /**\n     * A property used to control the visibility of the vertical scrollbar.\n     *\n     * @return true if the scrollbar should be shown to the user\n     */\n    public final BooleanProperty showScrollBarProperty() {\n        return showScrollBar;\n    }\n\n    /**\n     * Sets the value of {@link #showScrollBarProperty()}.\n     *\n     * @param show if true the scrollbar will be visible\n     */\n    public final void setShowScrollBar(boolean show) {\n        this.showScrollBar.set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showScrollBarProperty()}.\n     *\n     * @return true if the scrollbar will be visible\n     */\n    public final boolean isShowScrollBar() {\n        return showScrollBar.get();\n    }\n\n    // start date support\n\n    private final ReadOnlyObjectWrapper<LocalDate> startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\");\n\n    /**\n     * The earliest date shown by the view.\n     *\n     * @return the earliest date shown\n     */\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        return startDate;\n    }\n\n    /**\n     * Returns the value of {@link #startDateProperty()}.\n     *\n     * @return the earliest date shown\n     */\n    public final LocalDate getStartDate() {\n        return startDate.get();\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\");\n\n    // end date support\n\n    /**\n     * The latest date shown by the view.\n     *\n     * @return the latest date shown\n     */\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        return endDate;\n    }\n\n    /**\n     * Returns the value of {@link #endDateProperty()}.\n     *\n     * @return the latest date shown\n     */\n    public final LocalDate getEndDate() {\n        return endDate.get();\n    }\n\n    @Override\n    public void goForward() {\n        setDate(getDate().plusDays(getNumberOfDays()));\n    }\n\n    @Override\n    public void goBack() {\n        setDate(getDate().minusDays(getNumberOfDays()));\n    }\n\n    private static final String WEEK_VIEW_CATEGORY = \"Week View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(numberOfDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setNumberOfDays((Integer) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getNumberOfDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show All Day View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show All Day View\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowAllDayView();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowAllDayView((boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showAllDayViewProperty());\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Time Scale View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Time Scale View\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowTimeScaleView();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowTimeScaleView((boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTimeScaleViewProperty());\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Weekday Header View\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Weekday Header View\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekDayHeaderView();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekDayHeaderView((boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekDayHeaderViewProperty());\n            }\n        });\n\n        items.add(new Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show ScrollBar\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show ScrollBar\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowScrollBar();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowScrollBar((boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showScrollBarProperty());\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(adjustToFirstDayOfWeekProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setAdjustToFirstDayOfWeek((Boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isAdjustToFirstDayOfWeek();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Adjust to first day of week\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Adjust to first day of week\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DeveloperConsole.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.DeveloperConsoleSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.TabPane;\n\n/**\n * A control used for showing the internals of CalendarFX at work. Helps\n * detecting problems. Developers can freely add their own tabs to the tab pane.\n * <h3>Screenshot</h3>\n * <img alt=\"Developer Console\" src=\"doc-files/developer-console.png\">\n */\npublic class DeveloperConsole extends CalendarFXControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"developer-console\";\n\n    private TabPane tabPane = new TabPane();\n\n    /**\n     * Constructs a new view.\n     */\n    public DeveloperConsole() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        this.tabPane = new TabPane();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new DeveloperConsoleSkin(this);\n    }\n\n    /**\n     * Returns the tab pane used by the console to display different sections.\n     * Subclasses can simply add their own tabs.\n     *\n     * @return the tab pane\n     */\n    public final TabPane getTabPane() {\n        return tabPane;\n    }\n\n    private final ObjectProperty<DateControl> dateControl = new SimpleObjectProperty<>(\n            this, \"dateControl\");\n\n    /**\n     * Stores a reference to the date control that will be monitored by the\n     * console.\n     *\n     * @return the date control\n     */\n    public final ObjectProperty<DateControl> dateControlProperty() {\n        return dateControl;\n    }\n\n    /**\n     * Sets the value of {@link #dateControlProperty()}.\n     *\n     * @param control\n     *            the control\n     */\n    public final void setDateControl(DateControl control) {\n        dateControlProperty().set(control);\n    }\n\n    /**\n     * Returns the value of {@link #dateControlProperty()}.\n     *\n     * @return the date control\n     */\n    public final DateControl getDateControl() {\n        return dateControlProperty().get();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/DraggedEntry.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\n\nimport java.time.Duration;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * Dragged entry is used internally only to represent the calendar entry that is\n * currently being dragged. The entry wraps the original entry and stores some\n * additional information about the ongoing drag.\n */\npublic final class DraggedEntry extends Entry<Object> {\n\n    /**\n     * An enum used for defining which drag operation is currently in progress.\n     */\n    public enum DragMode {\n\n        /**\n         * The user is changing the start time of an entry.\n         */\n        START_TIME,\n\n        /**\n         * The user is changing the end time of an entry.\n         */\n        END_TIME,\n\n        /**\n         * The user is dragging the entire entry, hence changing start and end\n         * time at the same time.\n         */\n        START_AND_END_TIME\n    }\n\n    private Duration offsetDuration;\n    private final Entry<?> originalEntry;\n    private final Calendar originalCalendar;\n    private DragMode dragMode;\n\n    /**\n     * Constructs a new dragged entry\n     *\n     * @param entry\n     *            the original entry being dragged\n     * @param dragMode\n     *            the drag mode (start time, end time, or both)\n     */\n    public DraggedEntry(Entry<?> entry, DragMode dragMode) {\n        this.originalEntry = requireNonNull(entry);\n        this.originalCalendar = requireNonNull(entry.getCalendar());\n        this.dragMode = dragMode;\n\n        setTitle(entry.getTitle());\n        setUserObject(entry.getUserObject());\n        setFullDay(entry.isFullDay());\n        setInterval(entry.getInterval());\n        setZoneId(entry.getZoneId());\n\n        getStyleClass().add(\"dragged-entry\");\n    }\n\n    /**\n     * Returns the current drag mode (start time, end time, or both).\n     *\n     * @return the drag mode\n     */\n    public DragMode getDragMode() {\n        return dragMode;\n    }\n\n    /**\n     * Sets the current drag mode (start time, end time, or both).\n     *\n     * @param dragMode\n     *            the drag mode\n     */\n    public void setDragMode(DragMode dragMode) {\n        requireNonNull(dragMode);\n        this.dragMode = dragMode;\n    }\n\n    /**\n     * Returns the original entry that the user wants to edit.\n     *\n     * @return the original calendar entry\n     */\n    public Entry<?> getOriginalEntry() {\n        return originalEntry;\n    }\n\n    /**\n     * Returns the original calendar where the entry is located that is being\n     * dragged.\n     *\n     * @return the calendar where the entry originated from\n     */\n    public Calendar getOriginalCalendar() {\n        return originalCalendar;\n    }\n\n    /**\n     * Sets the duration between the mouse press location and the start time of\n     * the entry.\n     *\n     * @param duration\n     *            the offset duration\n     */\n    public void setOffsetDuration(Duration duration) {\n        this.offsetDuration = duration;\n    }\n\n    /**\n     * Returns the duration between the mouse press location and the start time\n     * of the entry.\n     *\n     * @return the offset duration\n     */\n    public Duration getOffsetDuration() {\n        return offsetDuration;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/EntryViewBase.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl.EntryContextMenuParameter;\nimport com.calendarfx.view.DateControl.EntryDetailsParameter;\nimport com.calendarfx.view.DateControl.Layer;\nimport com.calendarfx.view.DayViewBase.AvailabilityEditingEntryBehaviour;\nimport com.calendarfx.view.DayViewBase.OverlapResolutionStrategy;\nimport javafx.animation.ScaleTransition;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.binding.BooleanBinding;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyBooleanProperty;\nimport javafx.beans.property.ReadOnlyBooleanWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.MapChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableSet;\nimport javafx.collections.WeakListChangeListener;\nimport javafx.css.PseudoClass;\nimport javafx.geometry.Point2D;\nimport javafx.scene.CacheHint;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.input.ContextMenuEvent;\nimport javafx.scene.input.InputEvent;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.input.MouseEvent;\nimport javafx.util.Callback;\nimport javafx.util.Duration;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\nimport static javafx.scene.control.SelectionMode.MULTIPLE;\nimport static javafx.scene.input.MouseButton.PRIMARY;\n\n/**\n * The base class for all views that are representing calendar entries. There\n * are specializations of this class for the {@link DayView}, the\n * {@link DetailedWeekView}, and the {@link MonthView}. Each date control class uses\n * their own entry factory to create entry view instances.\n * <p>\n * This view uses four pseudo classes:\n * <ul>\n * <li>dragged - when the user drags the view</li>\n * <li>dragged-start - when the user changes the start time of the view</li>\n * <li>dragged-end - when the user changes the end time of the view</li>\n * </ul>\n * These states can be used to visualize the view differently during editing\n * operations. The default styling causes the original view of a dragged entry\n * to lower its opacity so that the user can still see the view at its original\n * location as a \"ghost\". If the user only changes the start or end time then\n * the opacity of the original entry view will be set to 0, which means the view\n * will be invisible.\n *\n * @param <T> the type of date control where the entry is being used\n * @see DayView#entryViewFactoryProperty()\n * @see MonthView#entryViewFactoryProperty()\n */\npublic abstract class EntryViewBase<T extends DateControl> extends CalendarFXControl implements Comparable<EntryViewBase<T>> {\n\n    private static final PseudoClass DRAGGED_PSEUDO_CLASS = PseudoClass.getPseudoClass(\"dragged\");\n\n    private static final PseudoClass DRAGGED_START_PSEUDO_CLASS = PseudoClass.getPseudoClass(\"dragged-start\");\n\n    private static final PseudoClass DRAGGED_END_PSEUDO_CLASS = PseudoClass.getPseudoClass(\"dragged-end\");\n\n    private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass(\"selected\");\n\n    private Entry<?> entry;\n\n    private final ListChangeListener<? super String> styleListener = change -> {\n        while (change.next()) {\n            if (change.wasAdded()) {\n                getStyleClass().addAll(change.getAddedSubList());\n            } else if (change.wasRemoved()) {\n                getStyleClass().removeAll(change.getRemoved());\n            }\n        }\n    };\n\n    private final WeakListChangeListener<? super String> weakStyleListener = new WeakListChangeListener<>(styleListener);\n\n    private final InvalidationListener bindVisibilityListener = it -> bindVisibility();\n\n    private final WeakInvalidationListener weakBindVisibilityListener = new WeakInvalidationListener(bindVisibilityListener);\n\n    /**\n     * Constructs a new view for the given entry.\n     *\n     * @param entry the calendar entry\n     */\n    protected EntryViewBase(Entry<?> entry) {\n        setEntry(entry);\n\n        getStyleClass().addAll(entry.getStyleClass());\n\n        setFocusTraversable(true);\n\n        focusedProperty().addListener(it -> processFocus());\n\n        addEventHandler(MouseEvent.MOUSE_CLICKED, evt -> {\n            if (evt.getButton().equals(PRIMARY) && evt.isStillSincePress() && evt.getClickCount() == getDetailsClickCount()) {\n                showDetails(evt, evt.getScreenX(), evt.getScreenY());\n            }\n            evt.consume();\n        });\n\n        addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, evt -> {\n            DateControl dateControl = getDateControl();\n            if (dateControl != null) {\n                /*\n                 * Normally date control should always exist when we are in this\n                 * situation, but in the samples there is no control.\n                 */\n                Callback<EntryContextMenuParameter, ContextMenu> callback = dateControl.getEntryContextMenuCallback();\n                if (callback != null) {\n                    EntryContextMenuParameter param = new EntryContextMenuParameter(evt, dateControl, EntryViewBase.this);\n                    ContextMenu menu = callback.call(param);\n                    if (menu != null) {\n                        menu.show(getScene().getWindow(), evt.getScreenX(), evt.getScreenY());\n                        evt.consume();\n                    }\n                }\n            }\n        });\n\n        @SuppressWarnings(\"unchecked\")\n        MapChangeListener<? super Object, ? super Object> propertiesListener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(\"startDate\")) {\n                    setStartDate((LocalDate) change.getValueAdded());\n                } else if (change.getKey().equals(\"endDate\")) {\n                    setEndDate((LocalDate) change.getValueAdded());\n                } else if (change.getKey().equals(\"startTime\")) {\n                    setStartTime((LocalTime) change.getValueAdded());\n                } else if (change.getKey().equals(\"endTime\")) {\n                    setEndTime((LocalTime) change.getValueAdded());\n                } else if (change.getKey().equals(\"position\")) {\n                    setPosition((Position) change.getValueAdded());\n                } else if (change.getKey().equals(\"dragged\")) {\n                    Boolean onOff = (Boolean) change.getValueAdded();\n                    dragged.set(onOff);\n                    getProperties().remove(\"dragged\");\n                } else if (change.getKey().equals(\"dragged-start\")) {\n                    Boolean onOff = (Boolean) change.getValueAdded();\n                    draggedStart.set(onOff);\n                    getProperties().remove(\"dragged-start\");\n                } else if (change.getKey().equals(\"dragged-end\")) {\n                    Boolean onOff = (Boolean) change.getValueAdded();\n                    draggedEnd.set(onOff);\n                    getProperties().remove(\"dragged-end\");\n                } else if (change.getKey().equals(\"selected\")) {\n                    Boolean onOff = (Boolean) change.getValueAdded();\n                    selected.set(onOff);\n                    getProperties().remove(\"selected\");\n                } else if (change.getKey().equals(\"hidden\")) {\n                    Boolean onOff = (Boolean) change.getValueAdded();\n                    setHidden(onOff);\n                    getProperties().remove(\"hidden\");\n                } else if (change.getKey().equals(\"control\")) {\n                    T control = (T) change.getValueAdded();\n                    setDateControl(control);\n                    getProperties().remove(\"control\");\n                }\n            }\n        };\n\n        getProperties().addListener(propertiesListener);\n\n        dateControlProperty().addListener((observable, oldControl, newControl) -> {\n            if (oldControl != null) {\n                oldControl.getSelections().removeListener(weakSelectionListener);\n                oldControl.draggedEntryProperty().removeListener(weakDraggedListener);\n            }\n            if (newControl != null) {\n                newControl.getSelections().addListener(weakSelectionListener);\n                newControl.draggedEntryProperty().addListener(weakDraggedListener);\n            }\n\n            bindVisibility();\n        });\n\n        addEventHandler(KeyEvent.KEY_PRESSED, evt -> {\n            if (evt.getCode() == KeyCode.ENTER) {\n                Point2D localToScreen = localToScreen(0, 0);\n                showDetails(evt, localToScreen.getX() + getWidth(), localToScreen.getY() + getHeight() / 2);\n            }\n        });\n\n        dateControlProperty().addListener(it -> {\n            ObservableSet<Entry<?>> selections = getDateControl().getSelections();\n            boolean contains = selections.contains(entry);\n            selected.set(contains);\n        });\n\n        addEventHandler(MouseEvent.MOUSE_PRESSED, this::performSelection);\n\n        bindEntry();\n        bindVisibility();\n\n        layerProperty().addListener(weakBindVisibilityListener);\n    }\n\n    public void setEntry(Entry<?> entry) {\n        this.entry = requireNonNull(entry);\n        this.entry.getStyleClass().addListener(weakStyleListener);\n        this.entry = entry;\n        bindEntry();\n        bindVisibility();\n    }\n\n    private final IntegerProperty detailsClickCount = new SimpleIntegerProperty(this, \"detailsClickCount\", 2);\n\n    public final int getDetailsClickCount() {\n        return detailsClickCount.get();\n    }\n\n    /**\n     * Determines the click count that is required to trigger the\n     * \"show details\" action.\n     *\n     * @return the \"show details\" click count\n     * @see DateControl#entryDetailsCallbackProperty()\n     */\n    public final IntegerProperty detailsClickCountProperty() {\n        return detailsClickCount;\n    }\n\n    public final void setDetailsClickCount(int detailsClickCount) {\n        this.detailsClickCount.set(detailsClickCount);\n    }\n\n    /**\n     * Returns the calendar entry for which the view was created.\n     *\n     * @return the calendar entry\n     */\n    public final Entry<?> getEntry() {\n        return entry;\n    }\n\n    private void bindEntry() {\n        setStartDate(entry.getStartDate());\n        setEndDate(entry.getEndDate());\n        setStartTime(entry.getStartTime());\n        setEndTime(entry.getEndTime());\n\n        if (entry instanceof DraggedEntry) {\n            /*\n             * We want to make sure the dragged entry gets styled like a\n             * selected entry.\n             */\n            getProperties().put(\"selected\", true);\n        }\n\n        entry.hiddenProperty().addListener(weakBindVisibilityListener);\n        entry.calendarProperty().addListener(weakBindVisibilityListener);\n    }\n\n    private void bindVisibility() {\n        Entry<?> entry = getEntry();\n\n        T dateControl = getDateControl();\n\n        if (entry != null && dateControl != null) {\n            Calendar calendar = entry.getCalendar();\n\n            // the entry view can be hidden\n            BooleanBinding binding = Bindings.createBooleanBinding(() -> !isHidden(), hiddenProperty());\n\n            if (calendar != null) {\n                // the calendar can be hidden\n                binding = binding.and(dateControl.getCalendarVisibilityProperty(calendar));\n            }\n\n            // the entry itself can also be hidden\n            binding = binding.and(entry.hiddenProperty().not());\n\n            if (getLayer() != null) {\n                binding = binding.and(Bindings.createBooleanBinding(this::isAssignedLayerVisible, dateControl.visibleLayersProperty()));\n            }\n\n            if (dateControl instanceof DayViewBase) {\n                /*\n                 * Day views support editing of an availability calendar. During editing the\n                 * entries might be shown, hidden, or become somewhat transparent.\n                 */\n                DayViewBase dayView = (DayViewBase) dateControl;\n\n                binding = binding.and(dayView.editAvailabilityProperty().not().or(dayView.entryViewAvailabilityEditingBehaviourProperty().isEqualTo(AvailabilityEditingEntryBehaviour.HIDE).not()));\n            }\n\n            visibleProperty().bind(binding);\n        }\n    }\n\n    private boolean isAssignedLayerVisible() {\n        return getDateControl().visibleLayersProperty().contains(getLayer());\n    }\n\n    private boolean _hidden = false;\n\n    private ReadOnlyBooleanWrapper hidden;\n\n    /**\n     * A property set internally to indicate that the view could not be shown to\n     * the user. Most likely because there wasn't enough space (common case in\n     * {@link MonthView} where space is restricted).\n     *\n     * @return a read-only property used as a flag to signal whether the view is\n     * hidden or not\n     */\n    public final ReadOnlyBooleanProperty hiddenProperty() {\n        if (hidden == null) {\n            hidden = new ReadOnlyBooleanWrapper(this, \"hidden\", _hidden);\n        }\n        return hidden.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #hiddenProperty()}.\n     *\n     * @return true if the view is currently hidden because of insufficient\n     * space\n     */\n    public final boolean isHidden() {\n        return hidden == null ? _hidden : hidden.get();\n    }\n\n    private void setHidden(boolean b) {\n        if (hidden == null) {\n            _hidden = b;\n        } else {\n            hidden.set(b);\n        }\n    }\n\n    private void processFocus() {\n        if (isFocused()) {\n\n            if (!getProperties().containsKey(\"disable-focus-handling\")) {\n                DateControl control = getDateControl();\n                if (control != null) {\n                    if (!control.getSelections().contains(getEntry())) {\n                        control.getSelections().clear();\n                        control.select(getEntry());\n                    }\n                }\n\n                bounce();\n            }\n\n        }\n    }\n\n    /**\n     * Makes the entry view \"bounce\" by applying a scale transition. This is a\n     * good way to make an entry stand out, e.g. when it receives the keyboard\n     * focus.\n     */\n    public final void bounce() {\n        if (isEnableBounce()) {\n            ScaleTransition transition = new ScaleTransition(Duration.millis(200), this);\n            setCache(true);\n            setCacheHint(CacheHint.SCALE);\n            transition.setAutoReverse(true);\n            transition.setFromX(1);\n            transition.setToX(.8);\n            transition.setFromY(1);\n            transition.setToY(.8);\n            transition.setCycleCount(2);\n            transition.setOnFinished(evt -> setCache(false));\n            transition.play();\n        }\n    }\n\n    private final BooleanProperty enableBounce = new SimpleBooleanProperty(this, \"enableBounce\", false);\n\n    public final boolean isEnableBounce() {\n        return enableBounce.get();\n    }\n\n    /**\n     * Controls whether the entry should use a scale transition to bounce when it receives the\n     * focus. The default is false.\n     *\n     * @return true if the entry should bounce\n     */\n    public final BooleanProperty enableBounceProperty() {\n        return enableBounce;\n    }\n\n    public final void setEnableBounce(boolean enableBounce) {\n        this.enableBounce.set(enableBounce);\n    }\n\n    private final InvalidationListener selectionListener = it -> updateSelection();\n\n    private final WeakInvalidationListener weakSelectionListener = new WeakInvalidationListener(selectionListener);\n\n    private final InvalidationListener draggedListener = it -> updateDragged();\n\n    private final WeakInvalidationListener weakDraggedListener = new WeakInvalidationListener(draggedListener);\n\n    private void updateSelection() {\n        DateControl control = getDateControl();\n        if (control != null) {\n            Entry<?> entry = getEntry();\n\n            selected.set(control.getSelections().contains(entry));\n        }\n    }\n\n    private void updateDragged() {\n        DateControl control = getDateControl();\n        if (control != null) {\n            DraggedEntry draggedEntry = control.getDraggedEntry();\n            if (draggedEntry != null) {\n                if (draggedEntry.getOriginalEntry().equals(getEntry())) {\n                    switch (draggedEntry.getDragMode()) {\n                        case END_TIME:\n                            draggedEnd.set(true);\n                            break;\n                        case START_AND_END_TIME:\n                            dragged.set(true);\n                            break;\n                        case START_TIME:\n                            draggedStart.set(true);\n                            break;\n                        default:\n                            break;\n                    }\n                } else {\n                    dragged.set(false);\n                    draggedStart.set(false);\n                    draggedEnd.set(false);\n                }\n            } else {\n                dragged.set(false);\n                draggedStart.set(false);\n                draggedEnd.set(false);\n            }\n        }\n    }\n\n    private void showDetails(InputEvent evt, double x, double y) {\n        DateControl control = getDateControl();\n\n        /*\n         * We might run in the sampler application. Then the entry view will not\n         * be inside a date control.\n         */\n        if (control != null && getParent() != null) {\n            Callback<EntryDetailsParameter, Boolean> callback = control.getEntryDetailsCallback();\n            EntryDetailsParameter param = new EntryDetailsParameter(evt, control, getEntry(), this, this, x, y);\n            callback.call(param);\n        }\n    }\n\n    /**\n     * An enumerator used for specifying the position of an entry view. A\n     * calendar entry can span multiple days. The position can be used if the\n     * view is shown on the \"first\" day, the \"last\" day, or some day in the\n     * \"middle\" of the span. If the entry is located on only one day then the\n     * position will be \"only\".\n     * <p>\n     * The image below illustrates this concept:\n     *\n     * <img src=\"doc-files/multi-days.png\" alt=\"Multi Days\">\n     *\n     * @see EntryViewBase#positionProperty()\n     */\n    public enum Position {\n\n        /**\n         * Used when the calendar entry spans multiple days and the entry view\n         * is the shown on the first day.\n         */\n        FIRST,\n\n        /**\n         * Used when the calendar entry spans multiple days and the entry view\n         * is shown on one of the days in the middle.\n         */\n        MIDDLE,\n\n        /**\n         * Used when the calendar entry spans multiple days and the entry view\n         * is the shown on the last day.\n         */\n        LAST,\n\n        /**\n         * Used when the calendar entry only spans a single day and the entry\n         * view is shown on that day.\n         */\n        ONLY\n    }\n\n    private Position _position = Position.ONLY;\n\n    private ReadOnlyObjectWrapper<Position> position;\n\n    /**\n     * A calendar entry can span multiple days. The position can be used if the\n     * view is shown on the \"first\" day, the \"last\" day, or some day in the\n     * \"middle\" of the span. If the entry is located on only one day then the\n     * position will be \"only\". This property is read-only and will be set by\n     * the framework.\n     * <p>\n     * The image below illustrates this concept:\n     *\n     * <img src=\"doc-files/multi-days.png\" alt=\"Multi Day\">\n     *\n     * @return the position of the view within the time range of the calendar\n     * entry\n     */\n    public final ReadOnlyObjectProperty<Position> positionProperty() {\n        if (position == null) {\n            position = new ReadOnlyObjectWrapper<>(this, \"position\", _position);\n        }\n        return position.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #positionProperty()}.\n     *\n     * @return the position\n     */\n    public final Position getPosition() {\n        return position == null ? _position : position.get();\n    }\n\n    private void setPosition(Position pos) {\n        if (position == null) {\n            _position = pos;\n        } else {\n            position.set(pos);\n        }\n    }\n\n    private T _dateControl;\n\n    private ReadOnlyObjectWrapper<T> dateControl;\n\n    /**\n     * The date control where the entry view is shown.\n     *\n     * @return the date control\n     */\n    public final ReadOnlyObjectProperty<T> dateControlProperty() {\n        if (dateControl == null) {\n            dateControl = new ReadOnlyObjectWrapper<>(this, \"dateControl\", _dateControl);\n        }\n\n        return dateControl.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #dateControlProperty()}.\n     *\n     * @return the date control\n     */\n    public final T getDateControl() {\n        return dateControl == null ? _dateControl : dateControl.get();\n    }\n\n    private void setDateControl(T control) {\n        if (dateControl == null) {\n            _dateControl = control;\n        } else {\n            dateControl.set(control);\n        }\n    }\n\n    private LocalDate _startDate;\n\n    private ReadOnlyObjectWrapper<LocalDate> startDate;\n\n    /**\n     * The date where the view starts to appear (not the start date of the\n     * calendar entry). In most views the start and end date of an entry view\n     * are the same date (the date shown by the control). However, the\n     * {@link AllDayView} lays out entry views across multiple days.\n     *\n     * @return the start date of the view\n     */\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        if (startDate == null) {\n            startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\", _startDate);\n        }\n        return startDate.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #startDateProperty()}.\n     *\n     * @return the start date of the view (not of the calendar entry)\n     */\n    public final LocalDate getStartDate() {\n        return startDate == null ? _startDate : startDate.get();\n    }\n\n    private void setStartDate(LocalDate date) {\n        if (startDate == null) {\n            _startDate = date;\n        } else {\n            startDate.set(date);\n        }\n    }\n\n    private LocalDate _endDate;\n\n    private ReadOnlyObjectWrapper<LocalDate> endDate;\n\n    /**\n     * The date where the view stops to appear (not the end date of the calendar\n     * entry). In most views the start and end date of an entry view are the\n     * same date (the date shown by the control). However, the\n     * {@link AllDayView} lays out entry views across multiple days.\n     *\n     * @return the end date of the view\n     */\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        if (endDate == null) {\n            endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\", _endDate);\n        }\n\n        return endDate.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #endDateProperty()}.\n     *\n     * @return the end date of the view (not of the calendar entry)\n     */\n    public final LocalDate getEndDate() {\n        return endDate == null ? _endDate : endDate.get();\n    }\n\n    private void setEndDate(LocalDate date) {\n        if (endDate == null) {\n            _endDate = date;\n        } else {\n            endDate.set(date);\n        }\n    }\n\n    private LocalTime _startTime;\n\n    private ReadOnlyObjectWrapper<LocalTime> startTime;\n\n    /**\n     * The time where the entry view starts (not the start time of the calendar\n     * entry).\n     *\n     * @return the start time of the view (not of the calendar entry)\n     */\n    public final ReadOnlyObjectProperty<LocalTime> startTimeProperty() {\n        if (startTime == null) {\n            startTime = new ReadOnlyObjectWrapper<>(this, \"startTime\", _startTime);\n        }\n        return startTime.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #startTimeProperty()}.\n     *\n     * @return the start time of the view (not of the calendar entry)\n     */\n    public final LocalTime getStartTime() {\n        return startTime == null ? _startTime : startTime.get();\n    }\n\n    private void setStartTime(LocalTime time) {\n        if (startTime == null) {\n            _startTime = time;\n        } else {\n            startTime.set(time);\n        }\n    }\n\n    private LocalTime _endTime;\n\n    private ReadOnlyObjectWrapper<LocalTime> endTime;\n\n    /**\n     * The time where the entry view ends (not the end time of the calendar\n     * entry).\n     *\n     * @return the end time of the view (not of the calendar entry)\n     */\n    public final ReadOnlyObjectProperty<LocalTime> endTimeProperty() {\n        if (endTime == null) {\n            endTime = new ReadOnlyObjectWrapper<>(this, \"endTime\", _endTime);\n        }\n        return endTime.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #endTimeProperty()}.\n     *\n     * @return the end time of the view (not of the calendar entry)\n     */\n    public final LocalTime getEndTime() {\n        return endTime == null ? _endTime : endTime.get();\n    }\n\n    private void setEndTime(LocalTime time) {\n        if (endTime == null) {\n            _endTime = time;\n        } else {\n            endTime.set(time);\n        }\n    }\n\n    /*\n     * Dragged support.\n     */\n\n    private final ReadOnlyBooleanWrapper dragged = new ReadOnlyBooleanWrapper(false) {\n\n        @Override\n        protected void invalidated() {\n            pseudoClassStateChanged(DRAGGED_PSEUDO_CLASS, get());\n        }\n\n        @Override\n        public Object getBean() {\n            return EntryViewBase.this;\n        }\n\n        @Override\n        public String getName() {\n            return \"dragged\";\n        }\n    };\n\n    /**\n     * A flag used to indicate that the entry view is currently being dragged by\n     * the user. This property triggers the pseudo class \"dragged\".\n     *\n     * @return true if the entry is being dragged\n     */\n    public final ReadOnlyBooleanProperty draggedProperty() {\n        return dragged.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of the {@link #draggedProperty()}.\n     *\n     * @return true if the entry is being dragged\n     */\n    public final boolean isDragged() {\n        return draggedProperty().get();\n    }\n\n    /*\n     * Dragged start support.\n     */\n\n    private final ReadOnlyBooleanWrapper draggedStart = new ReadOnlyBooleanWrapper(false) {\n\n        @Override\n        protected void invalidated() {\n            pseudoClassStateChanged(DRAGGED_START_PSEUDO_CLASS, get());\n        }\n\n        @Override\n        public Object getBean() {\n            return EntryViewBase.this;\n        }\n\n        @Override\n        public String getName() {\n            return \"draggedStart\";\n        }\n    };\n\n    /**\n     * A flag used to indicate that the user is currently changing the start\n     * time of the entry (view). This property triggers the pseudo class\n     * \"dragged-start\".\n     *\n     * @return true if the entry start time is being changed\n     */\n    public final ReadOnlyBooleanProperty draggedStartProperty() {\n        return draggedStart.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #draggedStartProperty()}.\n     *\n     * @return true if the start time is changing\n     */\n    public final boolean isDraggedStart() {\n        return draggedStartProperty().get();\n    }\n\n    /*\n     * Dragged end support.\n     */\n\n    private final ReadOnlyBooleanWrapper draggedEnd = new ReadOnlyBooleanWrapper(false) {\n\n        @Override\n        protected void invalidated() {\n            pseudoClassStateChanged(DRAGGED_END_PSEUDO_CLASS, get());\n        }\n\n        @Override\n        public Object getBean() {\n            return EntryViewBase.this;\n        }\n\n        @Override\n        public String getName() {\n            return \"draggedEnd\";\n        }\n    };\n\n    /**\n     * A flag used to indicate that the user is currently changing the end time\n     * of the entry (view). This property triggers the pseudo class\n     * \"dragged-end\".\n     *\n     * @return true if the entry end time is being changed\n     */\n    public final ReadOnlyBooleanProperty draggedEndProperty() {\n        return draggedEnd.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #draggedEndProperty()}.\n     *\n     * @return true if the end time is changing\n     */\n    public final boolean isDraggedEnd() {\n        return draggedEndProperty().get();\n    }\n\n    /*\n     * Selected support.\n     */\n\n    private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(false) {\n\n        @Override\n        protected void invalidated() {\n            pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, get());\n        }\n\n        @Override\n        public Object getBean() {\n            return EntryViewBase.this;\n        }\n\n        @Override\n        public String getName() {\n            return \"selected\";\n        }\n    };\n\n    /**\n     * A flag used to indicate that the entry has been selected by the user.\n     * This property triggers the \"selected\" pseudo class.\n     *\n     * @return true if the entry view has been selected\n     * @see DateControl#getSelections()\n     */\n    public final ReadOnlyBooleanProperty selectedProperty() {\n        return selected.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #selectedProperty()}.\n     *\n     * @return true if the entry view is selected\n     */\n    public final boolean isSelected() {\n        return selectedProperty().get();\n    }\n\n    /**\n     * Entry presentation layer.\n     */\n\n    private Layer _layer = Layer.BASE;\n\n    private ObjectProperty<Layer> layer;\n\n    /**\n     * Layer on which entry will be presented. For possible values please check {@link Layer}.\n     *\n     * @return the entry presentation layer\n     */\n    public final ObjectProperty<Layer> layerProperty() {\n        if (layer == null) {\n            layer = new SimpleObjectProperty<>(this, \"layer\", _layer);\n        }\n        return layer;\n    }\n\n    /**\n     * Returns the value of {@link #layerProperty()}.\n     *\n     * @return the entry presentation layer\n     */\n    public final Layer getLayer() {\n        return layer == null ? _layer : layer.get();\n    }\n\n    /**\n     * Sets the value of {@link #layerProperty()}.\n     *\n     * @param layer the entry presentation layer\n     */\n    public final void setLayer(Layer layer) {\n        if (this.layer == null) {\n            _layer = layer;\n        } else {\n            this.layer.set(layer);\n        }\n    }\n\n    /**\n     * Width percentage of entry view.\n     */\n\n    private Double _widthPercentage = 100.0;\n\n    private DoubleProperty widthPercentage;\n\n    /**\n     * A percentage value used to specify how much of the available width inside the\n     * view will be utilized by the entry views. The default value is 100%, however\n     * applications might want to set a smaller value to allow the user to click and\n     * create new entries in already used time intervals.\n     * <p>\n     * Width percentage is only used for width computation when {@link #prefWidthProperty()}\n     * of view entry has no defined value and when {@link #alignmentStrategyProperty()}\n     * is not {@link AlignmentStrategy#FILL}.\n     * </p>\n     *\n     * @return the entry percentage width\n     */\n    public final DoubleProperty widthPercentageProperty() {\n        if (widthPercentage == null) {\n            widthPercentage = new SimpleDoubleProperty(this, \"widthPercentage\", _widthPercentage) {\n                @Override\n                public void set(double percentage) {\n                    validateWidthPercentageProperty(percentage);\n                    super.set(percentage);\n                }\n            };\n        }\n        return widthPercentage;\n    }\n\n    /**\n     * Returns the value of {@link #widthPercentageProperty()}.\n     *\n     * @return the entry percentage width\n     */\n    public double getWidthPercentage() {\n        return widthPercentage == null ? _widthPercentage : widthPercentage.get();\n    }\n\n    /**\n     * Sets the value of {@link #widthPercentage}.\n     *\n     * @param widthPercentage the new entry percentage width\n     */\n    public final void setWidthPercentage(double widthPercentage) {\n        if (this.widthPercentage == null) {\n            validateWidthPercentageProperty(widthPercentage);\n            _widthPercentage = widthPercentage;\n        } else {\n            this.widthPercentage.set(widthPercentage);\n        }\n    }\n\n    private void validateWidthPercentageProperty(double newValue) {\n        if (newValue < 0.0 || newValue > 100.0) {\n            throw new IllegalArgumentException(\"percentage width must be between 0 and 100 but was \" + newValue);\n        }\n    }\n\n    /**\n     * Different strategies for determining the height of an entry view. Normally\n     * the height of an entry is based on its start and end times. But sometimes\n     * we might want to simply use the start time for its location and the required\n     * height based on its content (e.g. the labels inside the entry view). The layout\n     * strategy {@link HeightLayoutStrategy#COMPUTE_PREF_SIZE} disables changes to the end\n     * time of the entry as the bottom y coordinate of the view would not accurately\n     * represent the end time of the entry.\n     *\n     * @see DayViewBase#setOverlapResolutionStrategy(OverlapResolutionStrategy)\n     */\n    public enum HeightLayoutStrategy {\n        USE_START_AND_END_TIME,\n        COMPUTE_PREF_SIZE,\n    }\n\n    private final ObjectProperty<HeightLayoutStrategy> heightLayoutStrategy = new SimpleObjectProperty<>(this, \"heightLayoutStrategy\", HeightLayoutStrategy.USE_START_AND_END_TIME);\n\n    public final HeightLayoutStrategy getHeightLayoutStrategy() {\n        return heightLayoutStrategy.get();\n    }\n\n    /**\n     * Stores the height layout strategy that will be used for this entry view. For\n     * more information see {@link HeightLayoutStrategy}.\n     *\n     * @return the entry view's height layout strategy\n     */\n    public final ObjectProperty<HeightLayoutStrategy> heightLayoutStrategyProperty() {\n        return heightLayoutStrategy;\n    }\n\n    public final void setHeightLayoutStrategy(HeightLayoutStrategy heightLayoutStrategy) {\n        this.heightLayoutStrategy.set(heightLayoutStrategy);\n    }\n\n    /**\n     * Different strategies for aligning the entry view inside its day view. Normally\n     * an entry view fills the entire width of a {@link DayView} but special cases might\n     * require the entry to simply use the preferred width of the view and align the\n     * entry's view on the left, the center, or the middle.\n     * <p>\n     * If the time intervals of two entries are overlapping then the entries might\n     * be placed in two columns. The alignment strategy would then determine the layout\n     * of the entry within its column.\n     * </p>\n     *\n     * @see #setAlignmentStrategy(AlignmentStrategy)\n     */\n    public enum AlignmentStrategy {\n        FILL,\n        ALIGN_LEFT,\n        ALIGN_RIGHT,\n        ALIGN_CENTER\n    }\n\n    private final ObjectProperty<AlignmentStrategy> alignmentStrategy = new SimpleObjectProperty<>(this, \"alignmentStrategy\", AlignmentStrategy.FILL);\n\n    public final AlignmentStrategy getAlignmentStrategy() {\n        return alignmentStrategy.get();\n    }\n\n    /**\n     * Stores the alignment strategy that will be used for this entry view. For\n     * more information see {@link AlignmentStrategy}.\n     *\n     * @return the entry view's alignment strategy\n     */\n    public final ObjectProperty<AlignmentStrategy> alignmentStrategyProperty() {\n        return alignmentStrategy;\n    }\n\n    public final void setAlignmentStrategy(AlignmentStrategy alignmentStrategy) {\n        this.alignmentStrategy.set(alignmentStrategy);\n    }\n\n    /**\n     * Convenience method to check if this entry view intersects with the given\n     * entry view. Delegates to {@link Entry#intersects(Entry)}.\n     *\n     * @param otherView the other view to check\n     * @return true if the time intervals of the two entries / views overlap each other\n     */\n    public final boolean intersects(EntryViewBase<?> otherView) {\n        return getEntry().intersects(otherView.getEntry());\n    }\n\n    /**\n     * Convenience method to determine whether the entry belongs to a calendar\n     * that is read-only.\n     *\n     * @return true if the entry can not be edited by the user\n     */\n    public final boolean isReadOnly() {\n        Entry<?> entry = getEntry();\n        Calendar calendar = entry.getCalendar();\n        if (calendar != null) {\n            return calendar.isReadOnly();\n        }\n\n        return false;\n    }\n\n    @Override\n    public int compareTo(EntryViewBase<T> o) {\n        return getEntry().compareTo(o.getEntry());\n    }\n\n    @Override\n    public String toString() {\n        return \"EntryViewBase [entry=\" + getEntry() + \", selected=\" + isSelected() + \"]\";\n    }\n\n    private static final String ENTRY_VIEW_CATEGORY = \"Entry View Base\";\n\n    /**\n     * Returns a list of property items that can be shown by the\n     * {@link PropertySheet} of ControlsFX.\n     *\n     * @return the property sheet items\n     */\n    public ObservableList<Item> getPropertySheetItems() {\n\n        ObservableList<Item> items = FXCollections.observableArrayList();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(positionProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                position.set((Position) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getPosition();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Position.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Position\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Position (first, last, middle, only)\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(selectedProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                selected.set((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isSelected();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Selected\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Selected\";\n            }\n\n            @Override\n            public String getCategory() {\n                return ENTRY_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n\n    private void performSelection(MouseEvent evt) {\n        if ((evt.getButton().equals(PRIMARY) || evt.isPopupTrigger()) && evt.getClickCount() == 1) {\n            String disableFocusHandlingKey = \"disable-focus-handling\";\n            getProperties().put(disableFocusHandlingKey, true);\n            requestFocus();\n\n            DateControl control = getDateControl();\n\n            if (control == null) {\n                return;\n            }\n\n            if (!isMultiSelect(evt) && !control.getSelections().contains(entry)) {\n                control.clearSelection();\n            }\n\n            if (isMultiSelect(evt) && control.getSelections().contains(entry)) {\n                control.deselect(entry);\n            } else {\n                control.getSelections().add(entry);\n            }\n\n            getProperties().remove(disableFocusHandlingKey);\n\n            evt.consume();\n        }\n    }\n\n    private boolean isMultiSelect(MouseEvent evt) {\n        return (evt.isShiftDown() || evt.isShortcutDown()) && getDateControl().getSelectionMode().equals(MULTIPLE);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/Messages.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport java.text.MessageFormat;\nimport java.util.MissingResourceException;\nimport java.util.ResourceBundle;\n\n/**\n * Utility class for looking up i18n strings.\n */\npublic class Messages {\n\n    private static final String BUNDLE_NAME = \"com.calendarfx.view.messages\";\n\n    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);\n\n    private Messages() {\n    }\n\n    /**\n     * Returns the translation for the given key.\n     *\n     * @param key the i18n key\n     * @return the translation\n     */\n    public static String getString(String key) {\n        try {\n            return RESOURCE_BUNDLE.getString(key);\n        } catch (MissingResourceException e) {\n            return '!' + key + '!';\n        }\n    }\n\n    public static String getString(String key, Object... args) {\n        try {\n            String message = RESOURCE_BUNDLE.getString(key);\n            return MessageFormat.format(message, args);\n        } catch (MissingResourceException e) {\n            return '!' + key + '!';\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/MonthEntryView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.MonthEntryViewSkin;\nimport javafx.scene.control.Skin;\n\n/**\n * A specialized entry view used by the {@link MonthView}.\n */\npublic class MonthEntryView extends EntryViewBase<MonthView> {\n\n    /**\n     * Constructs a new entry view.\n     *\n     * @param entry\n     *            the calendar entry for which the view will be created\n     */\n    public MonthEntryView(Entry<?> entry) {\n        super(entry);\n\n        setMouseTransparent(false);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new MonthEntryViewSkin(this);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/MonthSheetView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Calendar.Style;\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.MonthSheetViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.css.PseudoClass;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.input.ContextMenuEvent;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.PickResult;\nimport javafx.scene.layout.Region;\nimport javafx.scene.paint.Color;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Month;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZonedDateTime;\nimport java.time.format.TextStyle;\nimport java.time.temporal.WeekFields;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A view laying out months and dates in a spreadsheet style. An ideal way of displaying several\n * months or even a year. Each day is represented by a cell (see {@link DateCell}). Cells are\n * created by a cell factory and can be customized to fit an application's needs.\n * <h3>Screenshot (Using DetailedDateCell)</h3>\n * <img width=\"100%\" src=\"doc-files/month-sheet-view.png\" alt=\"Month Sheet View\">\n *\n * <h3>Screenshot (Using \"aligned\" weekday layout)</h3>\n * <img width=\"100%\" src=\"doc-files/month-sheet-view-aligned.png\" alt=\"Month Sheet View Aligned\">\n *\n * @see #setWeekDayLayout(WeekDayLayoutStrategy)\n * @see #setCellFactory(Callback)\n * @see MonthSheetView.DateCell\n * @see MonthSheetView.SimpleDateCell\n * @see MonthSheetView.DetailedDateCell\n * @see MonthSheetView.BadgeDateCell\n * @see MonthSheetView.UsageDateCell\n */\npublic class MonthSheetView extends DateControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"month-sheet-view\";\n\n    private static final String USAGE_VERY_LOW = \"usage-very-low\";\n    private static final String USAGE_LOW = \"usage-low\";\n    private static final String USAGE_MEDIUM = \"usage-medium\";\n    private static final String USAGE_HIGH = \"usage-high\";\n    private static final String USAGE_VERY_HIGH = \"usage-very-high\";\n\n\n    private double ctxMenuScreenX;\n    private double ctxMenuScreenY;\n    private DateCell dateCell;\n\n    /**\n     * Constructs a new month sheet view that is using the {@link SimpleDateCell}.\n     */\n    public MonthSheetView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n        setHeaderCellFactory(param -> new MonthHeaderCell(param.getView(), param.getYearMonth()));\n\n        /*\n         * This view has its own context menu.\n         */\n        setContextMenu(createContextMenu());\n        addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, evt -> {\n            final PickResult pickResult = evt.getPickResult();\n            final Node intersectedNode = pickResult.getIntersectedNode();\n            if (intersectedNode != null && intersectedNode instanceof DateCell) {\n                this.dateCell = (DateCell) intersectedNode;\n            } else {\n                this.dateCell = null;\n            }\n\n            ctxMenuScreenX = evt.getScreenX();\n            ctxMenuScreenY = evt.getScreenY();\n        });\n\n        // Set the factory AFTER the context menu has been created or the cell factory\n        // will be overridden again.\n        setCellFactory(param -> new SimpleDateCell(param.getView(), param.getDate()));\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new MonthSheetViewSkin(this);\n    }\n\n    private ContextMenu createContextMenu() {\n        ContextMenu contextMenu = new ContextMenu();\n        MenuItem newEntry = new MenuItem(Messages.getString(\"MonthSheetView.ADD_NEW_EVENT\"));\n        newEntry.setOnAction(evt -> {\n            LocalDate date = getDateSelectionModel().getLastSelected();\n            createEntryAt(ZonedDateTime.of(date, LocalTime.of(12, 0), getZoneId()));\n        });\n\n        contextMenu.getItems().add(newEntry);\n\n        contextMenu.getItems().add(new SeparatorMenuItem());\n\n        RadioMenuItem standardCellItem = new RadioMenuItem(Messages.getString(\"MonthSheetView.STANDARD_CELLS\"));\n        RadioMenuItem detailCellItem = new RadioMenuItem(Messages.getString(\"MonthSheetView.DETAIL_CELLS\"));\n        RadioMenuItem usageCellItem = new RadioMenuItem(Messages.getString(\"MonthSheetView.USAGE_CELLS\"));\n        RadioMenuItem badgeCellItem = new RadioMenuItem(Messages.getString(\"MonthSheetView.BADGE_CELLS\"));\n\n        standardCellItem.setOnAction(evt -> setCellFactory(param -> new SimpleDateCell(param.getView(), param.getDate())));\n        detailCellItem.setOnAction(evt -> setCellFactory(param -> new DetailedDateCell(param.getView(), param.getDate())));\n        usageCellItem.setOnAction(evt -> setCellFactory(param -> new UsageDateCell(param.getView(), param.getDate())));\n        badgeCellItem.setOnAction(evt -> setCellFactory(param -> new BadgeDateCell(param.getView(), param.getDate())));\n\n        ToggleGroup group = new ToggleGroup();\n        group.getToggles().addAll(standardCellItem, detailCellItem, usageCellItem, badgeCellItem);\n\n        contextMenu.getItems().addAll(standardCellItem, detailCellItem, usageCellItem, badgeCellItem);\n\n        standardCellItem.setSelected(true);\n\n        return contextMenu;\n    }\n\n    // cell factory support\n\n    private final ObjectProperty<Callback<DateParameter, DateCell>> cellFactory = new SimpleObjectProperty<>(this, \"cellFactory\");\n\n    /**\n     * A property used to store a reference to the cell factory for this view. The default cell factory\n     * simply returns an instance of type {@link SimpleDateCell}.\n     *\n     * @return the cell factory\n     */\n    public final ObjectProperty<Callback<DateParameter, DateCell>> cellFactoryProperty() {\n        return cellFactory;\n    }\n\n    /**\n     * Returns the value of {@link #cellFactoryProperty()}.\n     *\n     * @return the cell factory\n     */\n    public final Callback<DateParameter, DateCell> getCellFactory() {\n        return cellFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #cellFactoryProperty()}.\n     *\n     * @param factory the cell factory\n     */\n    public final void setCellFactory(Callback<DateParameter, DateCell> factory) {\n        requireNonNull(factory);\n        cellFactoryProperty().set(factory);\n    }\n\n    // header cell factory support\n\n    private final ObjectProperty<Callback<HeaderParameter, Node>> headerCellFactory = new SimpleObjectProperty<>(this, \"headerCellFactory\");\n\n    /**\n     * A property used to store a reference to the \"header\" cell factory for this view. The default cell factory\n     * simply returns an instance of type {@link MonthHeaderCell}.\n     *\n     * @return the cell factory\n     */\n    public final ObjectProperty<Callback<HeaderParameter, Node>> headerCellFactoryProperty() {\n        return headerCellFactory;\n    }\n\n    /**\n     * Returns the value of {@link #headerCellFactoryProperty()}.\n     *\n     * @return the \"header\" cell factory\n     */\n    public final Callback<HeaderParameter, Node> getHeaderCellFactory() {\n        return headerCellFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #headerCellFactoryProperty()}.\n     *\n     * @param factory the \"header\" cell factory\n     */\n    public final void setHeaderCellFactory(Callback<HeaderParameter, Node> factory) {\n        requireNonNull(factory);\n        headerCellFactoryProperty().set(factory);\n    }\n\n    // layout strategy support\n\n    /**\n     * The different ways the view can align date cells.\n     *\n     * @see #setWeekDayLayout(WeekDayLayoutStrategy)\n     */\n    public enum WeekDayLayoutStrategy {\n\n        /**\n         * Aligns the date cells in the traditional ways where\n         * the top-most cell shows the 1st day of the month and the\n         * bottom-most cell shows the last day of the month.\n         */\n        STANDARD,\n\n        /**\n         * Aligns the date cells in such a way that all cells\n         * in a given row are dates on the same weekday, e.g. all\n         * cells / dates are located on a \"Monday\".\n         */\n        ALIGNED\n    }\n\n    private final ObjectProperty<WeekDayLayoutStrategy> weekDayLayout = new SimpleObjectProperty<>(this, \"weekDayLayout\", WeekDayLayoutStrategy.STANDARD);\n\n    /**\n     * A property used to store the layout strategy used by this view. The month sheet\n     * view is capable of ensuring that the date cells next to each other always show\n     * the same weekday. For this extra empty cells are added at the top of some of the\n     * columns. However, the standard layout is to always show the first day of the month\n     * in the first cell from the top.\n     *\n     * @return the weekday layout strategy property\n     */\n    public final ObjectProperty<WeekDayLayoutStrategy> weekDayLayoutProperty() {\n        return weekDayLayout;\n    }\n\n    /**\n     * Returns the value of {@link #weekDayLayoutProperty()}.\n     *\n     * @return the weekday layout strategy\n     */\n    public final WeekDayLayoutStrategy getWeekDayLayout() {\n        return weekDayLayoutProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #weekDayLayoutProperty()}.\n     *\n     * @param weekDayLayout the weekday layout strategy\n     */\n    public final void setWeekDayLayout(WeekDayLayoutStrategy weekDayLayout) {\n        weekDayLayoutProperty().set(weekDayLayout);\n    }\n\n    // view unit support\n\n    private final ObjectProperty<ViewUnit> viewUnit = new SimpleObjectProperty<>(this, \"viewUnit\", ViewUnit.YEAR);\n\n    /**\n     * A property used to store the unit shown by the view (e.g. \"quarters\", \"year\", \"month\").\n     *\n     * @return the view unit (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final ObjectProperty<ViewUnit> viewUnitProperty() {\n        return viewUnit;\n    }\n\n    /**\n     * Returns the value of {@link #viewUnitProperty()}.\n     *\n     * @return the view unit (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final ViewUnit getViewUnit() {\n        return viewUnitProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #viewUnitProperty()}.\n     *\n     * @param unit the view unit  (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final void setViewUnit(ViewUnit unit) {\n        requireNonNull(unit);\n        viewUnitProperty().set(unit);\n    }\n\n    // extended view unit support\n\n    private final ObjectProperty<ViewUnit> extendedViewUnit = new SimpleObjectProperty<>(this, \"extendedViewUnit\", ViewUnit.MONTH);\n\n    /**\n     * A property used to store the unit shown by the view in front of or after the\n     * main columns (e.g. \"quarters\", \"year\", \"month\"). So a month showing twelve months\n     * of a year might decide to add some \"padding\" and show another month in front of\n     * the year and one month after the year.\n     *\n     * @return the view unit (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final ObjectProperty<ViewUnit> extendedViewUnitProperty() {\n        return extendedViewUnit;\n    }\n\n    /**\n     * Returns the value of {@link #extendedViewUnitProperty()}.\n     *\n     * @return the extended view unit (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final ViewUnit getExtendedViewUnit() {\n        return extendedViewUnitProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #extendedViewUnitProperty()}.\n     *\n     * @param unit the extended view unit  (e.g. \"quarters\", \"year\", \"month\")\n     */\n    public final void setExtendedViewUnit(ViewUnit unit) {\n        requireNonNull(unit);\n        extendedViewUnitProperty().set(unit);\n    }\n\n    // extended units forward support\n\n    private final IntegerProperty extendedUnitsForward = new SimpleIntegerProperty(this, \"extendedUnitsForward\");\n\n    /**\n     * An integer property that stores the number of units that will be used\n     * to extend the view by. A month showing twelve months of a year might decide\n     * to add some \"padding\" and show another month at the end of the year. In this\n     * case the number of extended units will be 1.\n     *\n     * @return the number of units used to extend the view at the end (e.g. \"3 months\")\n     */\n    public final IntegerProperty extendedUnitsForwardProperty() {\n        return extendedUnitsForward;\n    }\n\n    /**\n     * Returns the value of {@link #extendedUnitsForward}.\n     *\n     * @return the number of units shown at the end of the view\n     */\n    public final int getExtendedUnitsForward() {\n        return extendedUnitsForwardProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #extendedUnitsForward}.\n     *\n     * @param units the number of units shown at the end of the view\n     */\n    public final void setExtendedUnitsForward(int units) {\n        if (units < 0) {\n            throw new IllegalArgumentException(\"number of units can not be negative but was \" + units);\n        }\n        extendedUnitsForwardProperty().set(units);\n    }\n\n    // extended units backward support\n\n    private final IntegerProperty extendedUnitsBackward = new SimpleIntegerProperty(this, \"extendedUnitsBackward\");\n\n    /**\n     * An integer property that stores the number of units that will be used\n     * to extend the view by. A month showing twelve months of a year might decide\n     * to put some \"padding\" in front of the year and show another month. In this\n     * case the number of extended units will be 1.\n     *\n     * @return the number of units used to extend the view at the beginning (e.g. \"3 months\")\n     */\n    public final IntegerProperty extendedUnitsBackwardProperty() {\n        return extendedUnitsBackward;\n    }\n\n    /**\n     * Returns the value of {@link #extendedUnitsBackwardProperty}.\n     *\n     * @return the number of units shown at the beginning of the view\n     */\n    public final int getExtendedUnitsBackward() {\n        return extendedUnitsBackwardProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #extendedUnitsBackwardProperty}.\n     *\n     * @param units the number of units shown at the beginning of the view\n     */\n    public final void setExtendedUnitsBackward(final int units) {\n        if (units < 0) {\n            throw new IllegalArgumentException(\"number of units can not be negative but was \" + units);\n        }\n        extendedUnitsBackwardProperty().set(units);\n    }\n\n    /**\n     * Returns the first \"regular\" month shown by the view. This method\n     * does not consider the extended months. If the view shows a whole\n     * year then the start month will be January.\n     *\n     * @return the start month\n     */\n    public final YearMonth getStartMonth() {\n        return getViewUnit().getStartMonth(getDate());\n    }\n\n    /**\n     * Returns the first \"regular\" month shown by the view. This method\n     * does not consider the extended months. If the view shows a whole\n     * year then the end month will be December.\n     *\n     * @return the start month\n     */\n    public final YearMonth getEndMonth() {\n        return getViewUnit().getEndMonth(getDate());\n    }\n\n    /**\n     * Returns the first month shown by the view. This method also takes the extended months into account. If the view shows a whole\n     * year with an extended unit of \"month\" and and extended unit count of two, then the start month will be November of the previous year.\n     *\n     * @return the start month\n     * @see #setExtendedViewUnit(ViewUnit)\n     * @see #setExtendedUnitsBackward(int)\n     * @see #setExtendedUnitsForward(int)\n     */\n    public final YearMonth getExtendedStartMonth() {\n        return getStartMonth().minusMonths(getExtendedViewUnit().toMonths(getExtendedUnitsBackward()));\n    }\n\n    /**\n     * Returns the last month shown by the view. This method also takes the extended months into account. If the view shows a whole\n     * year with an extended unit of \"month\" and and extended unit count of two, then the end month will be February of the next year.\n     *\n     * @return the start month\n     * @see #setExtendedViewUnit(ViewUnit)\n     * @see #setExtendedUnitsBackward(int)\n     * @see #setExtendedUnitsForward(int)\n     */\n    public final YearMonth getExtendedEndMonth() {\n        return getEndMonth().plusMonths(getExtendedViewUnit().toMonths(getExtendedUnitsForward()));\n    }\n\n    /**\n     * A simple check to see if the given month is part of the extended months.\n     *\n     * @param month the month to check\n     * @return true if the given month is part of the extended months\n     * @see #setExtendedViewUnit(ViewUnit)\n     * @see #setExtendedUnitsBackward(int)\n     * @see #setExtendedUnitsForward(int)\n     */\n    public final boolean isExtendedMonth(YearMonth month) {\n        if (month != null) {\n            YearMonth extendedStart = getExtendedStartMonth();\n            if ((month.equals(extendedStart) || month.isAfter(extendedStart)) && month.isBefore(getStartMonth())) {\n                return true;\n            }\n\n            YearMonth extendedEnd = getExtendedEndMonth();\n            return (month.equals(extendedEnd) || month.isBefore(extendedEnd)) && month.isAfter(getEndMonth());\n        }\n        return false;\n    }\n\n    /**\n     * Determines if the given date is currently showing is part of the view. This\n     * method uses the extended start and end months.\n     *\n     * @param date the date to check for visibility\n     * @return true if the date is within the time range of the view\n     * @see #getExtendedStartMonth()\n     * @see #getExtendedEndMonth()\n     */\n    public final boolean isVisibleDate(LocalDate date) {\n        if (date != null) {\n            YearMonth extendedStart = getExtendedStartMonth();\n            YearMonth extendedEnd = getExtendedEndMonth();\n\n            LocalDate startDate = extendedStart.atDay(1);\n            LocalDate endDate = extendedEnd.atEndOfMonth();\n\n            return (date.equals(startDate) || date.isAfter(startDate)) && (date.equals(endDate) || date.isBefore(endDate));\n        }\n        return false;\n    }\n\n    // show week number support\n\n    private final BooleanProperty showWeekNumber = new SimpleBooleanProperty(this, \"showWeekNumber\", true);\n\n    /**\n     * A property used to control whether the week numbers should be shown.\n     *\n     * @return true if the numbers should be shown\n     * @see DateControl#weekFieldsProperty()\n     */\n    public final BooleanProperty showWeekNumberProperty() {\n        return showWeekNumber;\n    }\n\n    /**\n     * Returns the value of {@link #showWeekNumberProperty()}.\n     *\n     * @return true if the numbers should be shown\n     */\n    public final boolean isShowWeekNumber() {\n        return showWeekNumberProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showWeekNumberProperty()}.\n     *\n     * @param show true if the numbers should be shown\n     */\n    public final void setShowWeekNumber(boolean show) {\n        showWeekNumberProperty().set(show);\n    }\n\n    // date selection model support\n\n    private final ObjectProperty<DateSelectionModel> dateSelectionModel = new SimpleObjectProperty<>(this, \"dateSelectionModel\", new DateSelectionModel());\n\n    /**\n     * A property used to store a selection model for selecting dates.\n     *\n     * @return the date selection model\n     */\n    public final ObjectProperty<DateSelectionModel> dateSelectionModelProperty() {\n        return dateSelectionModel;\n    }\n\n    /**\n     * Returns the value of {@link #dateSelectionModelProperty()}.\n     *\n     * @return the date selection model\n     */\n    public final DateSelectionModel getDateSelectionModel() {\n        return dateSelectionModelProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #dateSelectionModelProperty()}.\n     *\n     * @param model the date selection model\n     */\n    public final void setDateSelectionModel(DateSelectionModel model) {\n        Objects.requireNonNull(model);\n        dateSelectionModelProperty().set(model);\n    }\n\n    /**\n     * An enumerator to control the behaviour of the control when the user\n     * clicks on a date. The view supports date selections or the ability to\n     * show a popover that lists the entries on the clicked date.\n     *\n     * @see MonthSheetView#clickBehaviourProperty()\n     */\n    public enum ClickBehaviour {\n\n        /**\n         * A value used to make the control select the date on which the user clicked.\n         */\n        PERFORM_SELECTION,\n\n        /**\n         * A value used to make the control show some kind of dialog or popover to show\n         * details about the clicked date.\n         */\n        SHOW_DETAILS,\n\n        /**\n         * Do nothing when the user clicks on a date.\n         */\n        NONE\n    }\n\n    private final ObjectProperty<ClickBehaviour> clickBehaviour = new SimpleObjectProperty<>(this, \"clickBehaviour\", ClickBehaviour.PERFORM_SELECTION);\n\n    /**\n     * The behaviour used when the user clicks on a date.\n     *\n     * @return the click behaviour\n     */\n    public final ObjectProperty<ClickBehaviour> clickBehaviourProperty() {\n        return clickBehaviour;\n    }\n\n    /**\n     * Sets the value of {@link #clickBehaviourProperty()}.\n     *\n     * @param behaviour the click behaviour\n     */\n    public final void setClickBehaviour(ClickBehaviour behaviour) {\n        requireNonNull(behaviour);\n        clickBehaviourProperty().set(behaviour);\n    }\n\n    /**\n     * Returns the value of {@link #clickBehaviourProperty()}.\n     *\n     * @return the click behaviour\n     */\n    public final ClickBehaviour getClickBehaviour() {\n        return clickBehaviourProperty().get();\n    }\n\n    /**\n     * A view unit describes how many months the view should show. The view\n     * knows about years, semesters (6 months), quarters (3 months), and single\n     * months. When the date property changes the view will make sure to show\n     * the year, semester, quarter, or month where that date is located.\n     *\n     * @see MonthSheetView#setViewUnit(ViewUnit)\n     * @see MonthSheetView#setExtendedViewUnit(ViewUnit)\n     */\n    public enum ViewUnit {\n\n        /**\n         * A value used to instruct the {@link MonthSheetView} to display a single\n         * month.\n         *\n         * @see MonthSheetView#setViewUnit(ViewUnit)\n         */\n        MONTH {\n            @Override\n            public YearMonth getStartMonth(LocalDate date) {\n                return YearMonth.from(date);\n            }\n\n            @Override\n            public YearMonth getEndMonth(LocalDate date) {\n                return YearMonth.from(date);\n            }\n\n            @Override\n            public int getMonthsCount() {\n                return 1;\n            }\n        },\n\n        /**\n         * A value used to instruct the {@link MonthSheetView} to display a quarter\n         * year (3 months).\n         *\n         * @see MonthSheetView#setViewUnit(ViewUnit)\n         */\n        QUARTER {\n            @Override\n            public YearMonth getStartMonth(LocalDate date) {\n                return Year.of(date.getYear()).atMonth(QUARTER_START_MONTH[date.getMonthValue() - 1]);\n            }\n\n            @Override\n            public YearMonth getEndMonth(LocalDate date) {\n                return Year.of(date.getYear()).atMonth(QUARTER_END_MONTH[date.getMonthValue() - 1]);\n            }\n\n            @Override\n            public int getMonthsCount() {\n                return 3;\n            }\n        },\n\n        /**\n         * A value used to instruct the {@link MonthSheetView} to display a semester\n         * (6 months).\n         *\n         * @see MonthSheetView#setViewUnit(ViewUnit)\n         */\n        SEMESTER {\n            @Override\n            public YearMonth getStartMonth(LocalDate date) {\n                return Year.of(date.getYear()).atMonth(SEMESTER_START_MONTH[date.getMonthValue() - 1]);\n            }\n\n            @Override\n            public YearMonth getEndMonth(LocalDate date) {\n                return Year.of(date.getYear()).atMonth(SEMESTER_END_MONTH[date.getMonthValue() - 1]);\n            }\n\n            @Override\n            public int getMonthsCount() {\n                return 6;\n            }\n        },\n\n        /**\n         * A value used to instruct the {@link MonthSheetView} to display a whole year.\n         *\n         * @see MonthSheetView#setViewUnit(ViewUnit)\n         */\n        YEAR {\n            @Override\n            public YearMonth getStartMonth(LocalDate date) {\n                return Year.from(date).atMonth(Month.JANUARY);\n            }\n\n            @Override\n            public YearMonth getEndMonth(LocalDate date) {\n                return Year.from(date).atMonth(Month.DECEMBER);\n            }\n\n            @Override\n            public int getMonthsCount() {\n                return 12;\n            }\n        };\n\n        private static final int[] QUARTER_START_MONTH = {1, 1, 1, 4, 4, 4, 7, 7, 7, 10, 10, 10};\n        private static final int[] QUARTER_END_MONTH = {3, 3, 3, 6, 6, 6, 9, 9, 9, 12, 12, 12};\n        private static final int[] SEMESTER_START_MONTH = {1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7};\n        private static final int[] SEMESTER_END_MONTH = {6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12};\n\n        /**\n         * Returns the start month for the given view unit and date.\n         *\n         * @param date the date for which to return the start month\n         * @return the start month for the given view unit and date\n         */\n        public abstract YearMonth getStartMonth(LocalDate date);\n\n        /**\n         * Returns the end month for the given view unit and date.\n         *\n         * @param date the date for which to return the end month\n         * @return the end month for the given view unit and date\n         */\n        public abstract YearMonth getEndMonth(LocalDate date);\n\n        /**\n         * Returns the number of months represented by the view unit, e.g. \"3\" for\n         * \"quarter\".\n         *\n         * @return the month count of the view unit\n         */\n        public abstract int getMonthsCount();\n\n        /**\n         * Calculates the total number of months for the given\n         * number of units, e.g. when \"units\" is \"3\" and the \"view unit\" is\n         * \"quarter\" then the total number will be \"9\": three quarters equal\n         * 9 months.\n         *\n         * @param units the number of units\n         * @return the total number of months\n         */\n        public int toMonths(int units) {\n            return getMonthsCount() * units;\n        }\n    }\n\n    /**\n     * A parameter object used by the cell factory of the month sheet view.\n     */\n    public static final class DateParameter {\n\n        private final MonthSheetView view;\n        private final LocalDate date;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param view the view for which a cell has to be created\n         * @param date the date that the cell will represent\n         */\n        public DateParameter(MonthSheetView view, LocalDate date) {\n            this.view = Objects.requireNonNull(view);\n            this.date = date;\n        }\n\n        /**\n         * Returns the view for which a cell has to be created.\n         *\n         * @return the month sheet view\n         */\n        public MonthSheetView getView() {\n            return view;\n        }\n\n        /**\n         * Returns the date for which a cell has to be created.\n         *\n         * @return the date\n         */\n        public LocalDate getDate() {\n            return date;\n        }\n    }\n\n    /**\n     * A parameter object used by the \"header\" cell factory of the month sheet view.\n     */\n    public static final class HeaderParameter {\n\n        private final MonthSheetView view;\n        private final YearMonth yearMonth;\n\n        public HeaderParameter(MonthSheetView view, YearMonth yearMonth) {\n            this.view = Objects.requireNonNull(view);\n            this.yearMonth = yearMonth;\n        }\n\n        public MonthSheetView getView() {\n            return view;\n        }\n\n        public YearMonth getYearMonth() {\n            return yearMonth;\n        }\n    }\n\n    private static final String MONTH_SHEET_VIEW_CATEGORY = \"Month Sheet View\";\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(clickBehaviourProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setClickBehaviour((ClickBehaviour) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getClickBehaviour();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return ClickBehaviour.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Click Behaviour\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Click behaviour\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return WeekDayLayoutStrategy.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Week Day Layout\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The layout strategy for the week day on the months\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getWeekDayLayout();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setWeekDayLayout((WeekDayLayoutStrategy) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(weekDayLayoutProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return ViewUnit.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"View Unit\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"View Unit\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getViewUnit();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setViewUnit((ViewUnit) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(viewUnitProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return ViewUnit.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Extended View Unit\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Extended View Unit\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getExtendedViewUnit();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setExtendedViewUnit((ViewUnit) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(extendedViewUnitProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Extended Units Forward\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Extended Units Forward\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getExtendedUnitsForward();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setExtendedUnitsForward((Integer) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(extendedUnitsForwardProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Extended Units Backward\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Extended Units Backward\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getExtendedUnitsBackward();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setExtendedUnitsBackward((Integer) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(extendedUnitsBackwardProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Week Number\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Week Number\";\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekNumber();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekNumber((Boolean) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekNumberProperty());\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public Class<?> getType() {\n                return DateSelectionModel.SelectionMode.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_SHEET_VIEW_CATEGORY;\n            }\n\n            @Override\n            public String getName() {\n                return \"Date Selection Mode\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Date Selection Mode\";\n            }\n\n            @Override\n            public Object getValue() {\n                return getDateSelectionModel().getSelectionMode();\n            }\n\n            @Override\n            public void setValue(Object value) {\n                getDateSelectionModel().setSelectionMode((DateSelectionModel.SelectionMode) value);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(getDateSelectionModel().selectionModeProperty());\n            }\n        });\n\n        return items;\n    }\n\n    /**\n     * The default cell used for month headers.\n     *\n     * @see MonthSheetView#setHeaderCellFactory(Callback)\n     */\n    public static class MonthHeaderCell extends Label {\n\n        private final YearMonth yearMonth;\n        private final MonthSheetView view;\n\n        /**\n         * Constructs a new month header cell.\n         *\n         * @param view      the view where the header is needed\n         * @param yearMonth the month to display\n         */\n        public MonthHeaderCell(MonthSheetView view, YearMonth yearMonth) {\n            this(view, yearMonth, TextStyle.FULL);\n        }\n\n        /**\n         * Constructs a new month header cell.\n         *\n         * @param view      the view where the header is needed\n         * @param yearMonth the month to display\n         * @param textStyle the text style for the month label (full, short, ...)\n         */\n        public MonthHeaderCell(MonthSheetView view, YearMonth yearMonth, TextStyle textStyle) {\n            this.view = Objects.requireNonNull(view);\n            this.yearMonth = Objects.requireNonNull(yearMonth);\n            setText(yearMonth.getMonth().getDisplayName(textStyle, Locale.getDefault()));\n            setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n            setMinWidth(0);\n        }\n\n        /**\n         * Returns the view where the cell is used.\n         *\n         * @return the view\n         */\n        public final MonthSheetView getView() {\n            return view;\n        }\n\n        /**\n         * Returns the month for which the cell is used.\n         *\n         * @return the month\n         */\n        public final YearMonth getYearMonth() {\n            return yearMonth;\n        }\n\n    }\n\n    /**\n     * The base class for all date cells that are used in combination with the\n     * {@link MonthSheetView}. The base class provides support for the pseudo states\n     * \"today\" and \"selected\". The cell also adds several stylesheets based on the\n     * location of the cell and based on the date or weekday represented by the cell.\n     * <ul>\n     * <li><b>weekend-day</b> - if the date is on a weekend (e.g. a \"Saturday\" or \"Sunday\").</li>\n     * <li><b>extended-date-cell</b> - if the cell is located within an \"extension\" month.</li>\n     * <li><b>first-day-of-week</b> - if the day shown by the cell is the first day of the week (e.g. \"Monday\" in Europe, \"Sunday\" in the US).</li>\n     * </ul>\n     * The cell also automatically adds the weekday name as a style class to the date (\"monday\", \"tuesday\", ...).\n     */\n    public static abstract class DateCell extends Region {\n\n        private static final PseudoClass PSEUDO_CLASS_SELECTED = PseudoClass.getPseudoClass(\"selected\");\n        private static final PseudoClass PSEUDO_CLASS_TODAY = PseudoClass.getPseudoClass(\"today\");\n\n        private static final String CELL_STYLE_CLASS = \"date-cell\";\n        private static final String EXTENDED_CELL_STYLE_CLASS = \"extended-date-cell\";\n        private static final String WEEKEND_DAY = \"weekend-day\";\n        private static final String FIRST_DAY_OF_WEEK = \"first-day-of-week\";\n\n        private final LocalDate date;\n        private final MonthSheetView view;\n\n        private boolean selected;\n        private boolean today;\n\n        /**\n         * Constructs a new date cell.\n         *\n         * @param view the parent month sheet view\n         * @param date the date shown by the cell (might be null for leading or trailing empty cells)\n         */\n        public DateCell(MonthSheetView view, LocalDate date) {\n            this.view = Objects.requireNonNull(view);\n            this.date = date;\n\n            setMaxWidth(Double.MAX_VALUE);\n            getStyleClass().add(CELL_STYLE_CLASS);\n\n            setFocusTraversable(true);\n\n            applyStyles();\n        }\n\n        /**\n         * Returns the month sheet view to which the cell belongs.\n         *\n         * @return the parent month sheet view\n         */\n        public final MonthSheetView getView() {\n            return view;\n        }\n\n        /**\n         * Returns the date shown by the cell or null if it does\n         * not show any date (might be the case when used as a filler\n         * cell at the beginning or end of the month.\n         *\n         * @return the date shown by the cell\n         */\n        public final LocalDate getDate() {\n            return date;\n        }\n\n        private void applyStyles() {\n            YearMonth yearMonth;\n\n            if (date == null) {\n                // date can be null if cell is used to fill the month column\n                return;\n            }\n\n            yearMonth = YearMonth.from(date);\n\n            if (view.isExtendedMonth(yearMonth)) {\n                getStyleClass().add(EXTENDED_CELL_STYLE_CLASS);\n            }\n\n            WeekFields fields = view.getWeekFields();\n            DayOfWeek firstDayOfWeek = fields.getFirstDayOfWeek();\n            if (date.getDayOfWeek().equals(firstDayOfWeek)) {\n                getStyleClass().add(FIRST_DAY_OF_WEEK);\n            }\n\n            if (view.getWeekendDays().contains(date.getDayOfWeek())) {\n                if (!getStyleClass().contains(WEEKEND_DAY)) {\n                    getStyleClass().add(0, WEEKEND_DAY);\n                }\n            } else {\n                getStyleClass().remove(WEEKEND_DAY);\n            }\n\n            getStyleClass().add(date.getDayOfWeek().name().toLowerCase());\n        }\n\n        /**\n         * Returns true if the cell is currently selected.\n         *\n         * @return true if the cell is selected\n         */\n        public boolean isSelected() {\n            return selected;\n        }\n\n        /**\n         * Tells the cell whether it is currently selected or not.\n         *\n         * @param selected if true the cell will be considered \"selected\"\n         * @see MonthSheetView#getDateSelectionModel()\n         */\n        public void setSelected(boolean selected) {\n            /*\n             * Intentionally not made a final method. Cells might show\n             * different content when they are showing today or when they\n             * are not.\n             */\n            this.selected = selected;\n            pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, selected);\n        }\n\n        // Today support.\n\n        /**\n         * Returns true if the cell represents the day / date that is \"today\".\n         *\n         * @return true if the cell is showing \"today\"\n         * @see DateControl#todayProperty()\n         */\n        public boolean isToday() {\n            return today;\n        }\n\n        /**\n         * Specifies whether or not the cell represents the day / date that is \"today\".\n         * Applications are free to override this method but should always call\n         * super.setToday().\n         *\n         * @param today true tells the cell that it is showing \"today\"\n         * @see DateControl#todayProperty()\n         */\n        public void setToday(boolean today) {\n            /*\n             * Intentionally not made a final method. Cells might show\n             * different content when they are showing today or when they\n             * are not.\n             */\n            this.today = today;\n            pseudoClassStateChanged(PSEUDO_CLASS_TODAY, today);\n        }\n\n        /**\n         * This method gets invoked whenever the {@link MonthSheetView} determines\n         * that the content of the cell might need to be refreshed. This might be the\n         * case when new entries are created or existing entries are deleted. But also\n         * when an entry gets assigned to a new calendar or an entry's time interval\n         * has changed.\n         *\n         * @param entries the current list of entries for the day represented\n         *                by the cell\n         */\n        public void updateEntries(List<Entry<?>> entries) {\n        }\n    }\n\n    /**\n     * A specialization of the {@link SimpleDateCell} that adds a small canvas on the\n     * right-hand side to visualize the utilization of the day (the entries that exist on\n     * that date).\n     */\n    public static class DetailedDateCell extends SimpleDateCell {\n\n        private final DetailCanvas canvas;\n\n        private static final Map<String, Color> calendarColors = new HashMap<>();\n\n        static {\n            calendarColors.put(Style.STYLE1.name().toLowerCase(), Color.rgb(119, 192, 75, 0.8));\n            calendarColors.put(Style.STYLE2.name().toLowerCase(), Color.rgb(65, 143, 203, 0.8));\n            calendarColors.put(Style.STYLE3.name().toLowerCase(), Color.rgb(247, 209, 91, 0.8));\n            calendarColors.put(Style.STYLE4.name().toLowerCase(), Color.rgb(157, 91, 159, 0.8));\n            calendarColors.put(Style.STYLE5.name().toLowerCase(), Color.rgb(208, 82, 95, 0.8));\n            calendarColors.put(Style.STYLE6.name().toLowerCase(), Color.rgb(249, 132, 75, 0.8));\n            calendarColors.put(Style.STYLE7.name().toLowerCase(), Color.rgb(174, 102, 62, 0.8));\n        }\n\n        /**\n         * Constructs a new detailed date cell.\n         *\n         * @param view the parent month sheet view\n         * @param date the date shown by the cell (might be null for leading or trailing empty cells)\n         */\n        public DetailedDateCell(MonthSheetView view, LocalDate date) {\n            super(view, date);\n\n            canvas = new DetailCanvas();\n            canvas.setMouseTransparent(true);\n\n            getChildren().add(canvas);\n        }\n\n        /**\n         * Returns the color to be used for the given calendar style.\n         *\n         * @param style the calendar style\n         * @return the color to be used for the given calendar\n         * @see Calendar#setStyle(String)\n         */\n        public static final Color getCalendarColor(String style) {\n            return calendarColors.get(style);\n        }\n\n        /**\n         * Sets the color to be used for the given calendar style.\n         *\n         * @param style the calendar style\n         * @param color the color\n         */\n        public static final void setCalendarColor(String style, Color color) {\n            calendarColors.put(style, color);\n        }\n\n        @Override\n        protected void layoutChildren() {\n            Insets insets = getInsets();\n\n            double top = insets.getTop();\n            double bottom = insets.getBottom();\n            double left = insets.getLeft();\n            double right = insets.getRight();\n\n            double w = getWidth();\n            double h = getHeight();\n\n            double availableHeight = h - top - bottom;\n\n            double ps1 = dayOfMonthLabel.prefWidth(-1);\n            double ps2 = dayOfWeekLabel.prefWidth(-1);\n            double ps3 = weekNumberLabel.prefWidth(-1);\n            double ps4 = 12; // width of canvas\n\n            dayOfMonthLabel.resizeRelocate(left, top, ps1, availableHeight);\n            dayOfWeekLabel.resizeRelocate(left + ps1, top, ps2, availableHeight);\n            weekNumberLabel.resizeRelocate(w - right - ps3 - ps4, top, ps3, availableHeight);\n\n            // canvas\n            canvas.resizeRelocate(w - right - ps4, top, ps4, availableHeight);\n            canvas.setWidth(ps4);\n            canvas.setHeight(availableHeight);\n            canvas.draw();\n        }\n\n        @Override\n        protected double computePrefWidth(double height) {\n            return dayOfMonthLabel.prefWidth(-1) + dayOfWeekLabel.prefWidth(-1) + weekNumberLabel.prefWidth(-1) + 12;\n        }\n\n        /*\n         * Had to override because for some weird reason selected date cells were higher than deselected once.\n         */\n        @Override\n        protected double computePrefHeight(double width) {\n            // for the height we do not care about the canvas\n            double h = Math.max(dayOfMonthLabel.prefHeight(-1), Math.max(dayOfWeekLabel.prefHeight(-1), weekNumberLabel.prefHeight(-1)));\n            return h + getInsets().getTop() + getInsets().getBottom();\n        }\n\n        @Override\n        public void updateEntries(List<Entry<?>> entries) {\n            canvas.setEntries(entries);\n        }\n\n        private final class DetailCanvas extends Canvas {\n\n            private List<Entry<?>> entries;\n\n            private DetailCanvas() {\n                getStyleClass().add(\"detail-canvas\");\n                draw();\n            }\n\n            @Override\n            public boolean isResizable() {\n                return true;\n            }\n\n            public void setEntries(List<Entry<?>> entries) {\n                this.entries = entries;\n                draw();\n            }\n\n            public void draw() {\n                final double width = getWidth();\n                final double height = getHeight();\n\n                GraphicsContext gc = getGraphicsContext2D();\n                gc.clearRect(0, 0, width, height);\n\n                if (entries != null && !entries.isEmpty()) {\n                    for (Entry<?> entry : entries) {\n                        com.calendarfx.model.Calendar calendar = entry.getCalendar();\n                        if (calendar == null) {\n                            continue;\n                        }\n\n                        Color color = getCalendarColor(calendar.getStyle());\n                        gc.setFill(color);\n\n                        if (entry.isFullDay()) {\n                            gc.fillRect(0, 0, width, height);\n                        } else {\n                            LocalTime startTime = entry.getStartTime();\n                            LocalTime endTime = entry.getEndTime();\n\n                            if (entry.getStartDate().isBefore(getDate())) {\n                                startTime = LocalTime.MIN;\n                            }\n\n                            if (entry.getEndDate().isAfter(getDate())) {\n                                endTime = LocalTime.MAX;\n                            }\n\n                            double y = height * (startTime.toSecondOfDay() / (double) LocalTime.MAX.toSecondOfDay());\n                            double h = height * (endTime.toSecondOfDay() / (double) LocalTime.MAX.toSecondOfDay());\n                            gc.fillRect(0, y, width, h - y);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * The badge date cell extends the {@link SimpleDateCell} and adds another\n     * label to it that is used to display a counter of the number of entries that\n     * exist on that date.\n     */\n    public static class BadgeDateCell extends SimpleDateCell {\n\n        private final Label counterLabel;\n\n        /**\n         * Constructs a new badge date cell.\n         *\n         * @param view the parent month sheet view\n         * @param date the date shown by the cell (might be null for leading or trailing empty cells)\n         */\n        public BadgeDateCell(MonthSheetView view, LocalDate date) {\n            super(view, date);\n\n            getStyleClass().add(\"badge-date-cell\");\n\n            counterLabel = new Label();\n            counterLabel.getStyleClass().add(\"badge-label\");\n            counterLabel.setAlignment(Pos.CENTER_RIGHT);\n            counterLabel.setVisible(false); // has to be initially invisible (to work with empty cells)\n            getChildren().add(counterLabel);\n\n            // this cell type can not display week numbers\n            weekNumberLabel.setVisible(false);\n        }\n\n        @Override\n        protected void layoutChildren() {\n            Insets insets = getInsets();\n\n            double top = insets.getTop();\n            double bottom = insets.getBottom();\n            double left = insets.getLeft();\n            double right = insets.getRight();\n\n            double w = getWidth();\n            double h = getHeight();\n\n            double availableHeight = h - top - bottom;\n\n            double ps1 = dayOfMonthLabel.prefWidth(-1);\n            double ps2 = dayOfWeekLabel.prefWidth(-1);\n            double ps4 = counterLabel.prefWidth(-1);\n\n            double ph = counterLabel.prefHeight(-1);\n\n            dayOfMonthLabel.resizeRelocate(left, top, ps1, availableHeight);\n            dayOfWeekLabel.resizeRelocate(left + ps1, top, ps2, availableHeight);\n\n            // center the counter label, do not let it use the entire height\n            counterLabel.resizeRelocate(w - right - ps4, top + availableHeight / 2 - ph / 2, ps4, Math.min(availableHeight, ph));\n        }\n\n        @Override\n        protected double computePrefWidth(double height) {\n            return dayOfMonthLabel.prefWidth(-1) + dayOfWeekLabel.prefWidth(-1) + weekNumberLabel.prefWidth(-1);\n        }\n\n        /*\n         * Had to override because for some weird reason selected date cells were higher than deselected once.\n         */\n        @Override\n        protected double computePrefHeight(double width) {\n            double h = Math.max(counterLabel.prefHeight(-1), Math.max(dayOfMonthLabel.prefHeight(-1), Math.max(dayOfWeekLabel.prefHeight(-1), weekNumberLabel.prefHeight(-1))));\n            return h + getInsets().getTop() + getInsets().getBottom();\n        }\n\n        @Override\n        public void updateEntries(List<Entry<?>> entries) {\n            super.updateEntries(entries);\n\n            int entryCount = 0;\n            if (entries != null) {\n                entryCount = entries.size();\n            }\n\n            if (entryCount > 0) {\n                counterLabel.setText(Integer.toString(entries.size()));\n                counterLabel.setVisible(true);\n            } else {\n                counterLabel.setVisible(false);\n            }\n\n            counterLabel.getStyleClass().removeAll(USAGE_VERY_LOW, USAGE_LOW, USAGE_MEDIUM, USAGE_HIGH, USAGE_VERY_HIGH);\n\n            final Callback<Integer, DateControl.Usage> usagePolicy = getView().getUsagePolicy();\n\n            switch (usagePolicy.call(entryCount)) {\n                case NONE:\n                    break;\n                case VERY_LOW:\n                    counterLabel.getStyleClass().add(USAGE_VERY_LOW);\n                    break;\n                case LOW:\n                    counterLabel.getStyleClass().add(USAGE_LOW);\n                    break;\n                case MEDIUM:\n                    counterLabel.getStyleClass().add(USAGE_MEDIUM);\n                    break;\n                case HIGH:\n                    counterLabel.getStyleClass().add(USAGE_HIGH);\n                    break;\n                case VERY_HIGH:\n                default:\n                    counterLabel.getStyleClass().add(USAGE_VERY_HIGH);\n                    break;\n            }\n        }\n    }\n\n    /**\n     * A date cell used to display the day of month, the day of week, and the week of year in\n     * three separate labels. The styles used for these labels are \"day-of-month\", \"day-of-week\",\n     * and \"week-number\".\n     */\n    public static class SimpleDateCell extends DateCell {\n\n        private static final String DAY_OF_MONTH_STYLE = \"day-of-month-label\";\n        private static final String DAY_OF_WEEK_STYLE = \"day-of-week-label\";\n        private static final String WEEK_NUMBER_STYLE = \"week-number-label\";\n\n        protected final Label dayOfMonthLabel = new Label();\n        protected final Label dayOfWeekLabel = new Label();\n        protected final Label weekNumberLabel = new Label();\n\n        /**\n         * Constructs a new simple date cell.\n         *\n         * @param view the parent month sheet view\n         * @param date the date shown by the cell (might be null for leading or trailing empty cells)\n         */\n        public SimpleDateCell(MonthSheetView view, LocalDate date) {\n            super(view, date);\n\n            dayOfMonthLabel.getStyleClass().add(DAY_OF_MONTH_STYLE);\n            dayOfWeekLabel.getStyleClass().add(DAY_OF_WEEK_STYLE);\n            weekNumberLabel.getStyleClass().add(WEEK_NUMBER_STYLE);\n\n            dayOfMonthLabel.setManaged(false);\n            dayOfWeekLabel.setManaged(false);\n            weekNumberLabel.setManaged(false);\n\n            dayOfMonthLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n            dayOfWeekLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n            weekNumberLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n            dayOfMonthLabel.setMouseTransparent(!view.isEnableHyperlinks());\n            dayOfWeekLabel.setMouseTransparent(true);\n            weekNumberLabel.setMouseTransparent(true);\n\n            if (date != null) {\n                String dayOfWeekName = date.getDayOfWeek().name().toLowerCase();\n                dayOfMonthLabel.getStyleClass().add(dayOfWeekName + \"-label\");\n                dayOfWeekLabel.getStyleClass().add(dayOfWeekName + \"-label\");\n                weekNumberLabel.getStyleClass().add(dayOfWeekName + \"-label\");\n            }\n\n            getChildren().addAll(dayOfMonthLabel, dayOfWeekLabel, weekNumberLabel);\n\n            setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n            updateLabels();\n\n            if (view.isEnableHyperlinks()) {\n                dayOfMonthLabel.getStyleClass().add(\"date-hyperlink\");\n                dayOfMonthLabel.setOnMouseClicked(evt -> {\n                    if (evt.getButton() == MouseButton.PRIMARY && evt.getClickCount() == 1) {\n                        fireEvent(new RequestEvent(this, this, date));\n                    }\n                });\n            }\n        }\n\n        @Override\n        protected void layoutChildren() {\n            Insets insets = getInsets();\n\n            double top = insets.getTop();\n            double bottom = insets.getBottom();\n            double left = insets.getLeft();\n            double right = insets.getRight();\n\n            double w = getWidth();\n            double h = getHeight();\n\n            double availableHeight = h - top - bottom;\n\n            double ps1 = dayOfMonthLabel.prefWidth(-1);\n            double ps2 = dayOfWeekLabel.prefWidth(-1);\n            double ps3 = weekNumberLabel.prefWidth(-1);\n\n            dayOfMonthLabel.resizeRelocate(snapPositionX(left), snapPositionY(top), snapSizeX(ps1), snapSizeY(availableHeight));\n            dayOfWeekLabel.resizeRelocate(snapPositionX(left + ps1), snapPositionY(top), snapSizeX(ps2), snapSizeY(availableHeight));\n            weekNumberLabel.resizeRelocate(snapPositionX(w - right - ps3), snapPositionY(top), snapSizeX(ps3), snapSizeY(availableHeight));\n        }\n\n        @Override\n        protected double computePrefWidth(double height) {\n            return dayOfMonthLabel.prefWidth(-1) + dayOfWeekLabel.prefWidth(-1) + weekNumberLabel.prefWidth(-1);\n        }\n\n        /*\n         * Had to override because for some weird reason selected date cells were higher than deselected once.\n         */\n        @Override\n        protected double computePrefHeight(double width) {\n            double h = Math.max(dayOfMonthLabel.prefHeight(-1), Math.max(dayOfWeekLabel.prefHeight(-1), weekNumberLabel.prefHeight(-1)));\n            return h + getInsets().getTop() + getInsets().getBottom();\n        }\n\n        private void updateLabels() {\n            LocalDate date = getDate();\n            if (date == null) {\n                dayOfMonthLabel.setText(\"\");\n                dayOfWeekLabel.setText(\"\");\n                weekNumberLabel.setText(\"\");\n            } else {\n                dayOfMonthLabel.setText(String.valueOf(date.getDayOfMonth()));\n                dayOfWeekLabel.setText(date.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault()));\n\n                MonthSheetView view = getView();\n                if (view != null && view.isShowWeekNumber()) {\n                    WeekFields fields = view.getWeekFields();\n                    DayOfWeek firstDayOfWeek = fields.getFirstDayOfWeek();\n                    if (date.getDayOfWeek().equals(firstDayOfWeek)) {\n                        String weekNumber = Integer.toString(date.get(fields.weekOfYear()));\n                        weekNumberLabel.setText(weekNumber);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * A specialization of the {@link SimpleDateCell} that adds utilization information\n     * to the cell by coloring its background differently based on the number of entries\n     * found on that day. The cell uses the \"usage policy\" of the month sheet view to\n     * determine whether the utilization is low or high. The styles used for visualizing\n     * the different utilizations are \"usage-very-low\", \"usage-low\", \"usage-medium\",\n     * \"usage-high\", and \"usage-very-high\". If utilization is zero then none of these styles\n     * will be applied.\n     *\n     * @see DateControl#usagePolicyProperty()\n     */\n    public static class UsageDateCell extends SimpleDateCell {\n\n        /**\n         * Constructs a new usage date cell.\n         *\n         * @param view the parent month sheet view\n         * @param date the date shown by the cell (might be null for leading or trailing empty cells)\n         */\n        public UsageDateCell(MonthSheetView view, LocalDate date) {\n            super(view, date);\n        }\n\n        @Override\n        public void updateEntries(List<Entry<?>> entries) {\n\n            getStyleClass().removeAll(USAGE_VERY_LOW, USAGE_LOW, USAGE_MEDIUM, USAGE_HIGH, USAGE_VERY_HIGH);\n\n            int entryCount = 0;\n            if (entries != null) {\n                entryCount = entries.size();\n            }\n\n            final Callback<Integer, DateControl.Usage> usagePolicy = getView().getUsagePolicy();\n\n            switch (usagePolicy.call(entryCount)) {\n                case NONE:\n                    break;\n                case VERY_LOW:\n                    getStyleClass().add(USAGE_VERY_LOW);\n                    break;\n                case LOW:\n                    getStyleClass().add(USAGE_LOW);\n                    break;\n                case MEDIUM:\n                    getStyleClass().add(USAGE_MEDIUM);\n                    break;\n                case HIGH:\n                    getStyleClass().add(USAGE_HIGH);\n                    break;\n                case VERY_HIGH:\n                default:\n                    getStyleClass().add(USAGE_VERY_HIGH);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/MonthView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.MonthViewSkin;\nimport javafx.beans.Observable;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * Visualizes an entire month including the last days of the previous month and\n * the first days of the next month. Each day is shown in its own box with its\n * entries. These entries are of type {@link MonthEntryView}.\n *\n *\n * <img width=\"823\" src=\"doc-files/month-view.png\" alt=\"Month View\">\n *\n */\npublic class MonthView extends MonthViewBase implements ZonedDateTimeProvider {\n\n    private static final String DEFAULT_STYLE_CLASS = \"month-view\";\n\n    /**\n     * Constructs a new month view.\n     */\n    public MonthView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        setEntryViewFactory(MonthEntryView::new);\n\n        new CreateAndDeleteHandler(this);\n\n        getSelectedDates().addListener((Observable it) -> {\n            if (getSelectedDates().size() == 1) {\n                LocalDate date = getSelectedDates().iterator().next();\n                setDate(date);\n            }\n        });\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new MonthViewSkin(this);\n    }\n\n    @Override\n    public final ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId) {\n        MonthViewSkin skin = (MonthViewSkin) getSkin();\n        return skin.getZonedDateTimeAt(x, y, zoneId);\n    }\n\n    private final BooleanProperty showCurrentWeek = new SimpleBooleanProperty(\n            this, \"showCurrentWeek\", true);\n\n    /**\n     * Controls whether the view will highlight the current week. The image\n     * below shows an example:\n     *\n     * <img src=\"doc-files/current-week.png\" alt=\"Current Week\">\n     *\n     * @see DateControl#todayProperty()\n     *\n     * @return true if the current week will be highlighted\n     */\n    public final BooleanProperty showCurrentWeekProperty() {\n        return showCurrentWeek;\n    }\n\n    /**\n     * Sets the value of {@link #showCurrentWeekProperty()}.\n     *\n     * @param show\n     *            if true will highlight the current week\n     */\n    public final void setShowCurrentWeek(boolean show) {\n        showCurrentWeekProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showCurrentWeekProperty()}.\n     *\n     * @return true if the current week will be highlighted\n     */\n    public final boolean isShowCurrentWeek() {\n        return showCurrentWeekProperty().get();\n    }\n\n    private final BooleanProperty showWeekends = new SimpleBooleanProperty(\n            this, \"showWeekends\", true);\n\n    /**\n     * Controls whether the view will show weekend days differently than regular\n     * week days. The image below shows an example:\n     *\n     * <img src=\"doc-files/weekend.png\" alt=\"Weekend\">\n     *\n     * @return true if the weekend days will be shown differently\n     */\n    public final BooleanProperty showWeekendsProperty() {\n        return showWeekends;\n    }\n\n    /**\n     * Sets the value of {@link #showWeekendsProperty()}.\n     *\n     * @param show\n     *            if true will show weekend days differently\n     */\n    public final void setShowWeekends(boolean show) {\n        showWeekendsProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showWeekendsProperty()}.\n     *\n     * @return true if weekend days will be shown differently\n     */\n    public final boolean isShowWeekends() {\n        return showWeekendsProperty().get();\n    }\n\n    private final BooleanProperty showWeekdays = new SimpleBooleanProperty(\n            this, \"showWeekdays\", true);\n\n    /**\n     * Controls whether the view will show the names of the week days (\"Mo\",\n     * \"Tu\", \"We\", ...). The image below shows an example:\n     *\n     * <img src=\"doc-files/weekdays.png\" alt=\"Weekdays\">\n     *\n     * @return true if the week day names will be shown\n     */\n    public final BooleanProperty showWeekdaysProperty() {\n        return showWeekdays;\n    }\n\n    /**\n     * Sets the value of {@link #showWeekdaysProperty()}.\n     *\n     * @param show\n     *            if true will show the names of the week days\n     */\n    public final void setShowWeekdays(boolean show) {\n        showWeekdaysProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showWeekdaysProperty()}.\n     *\n     * @return true if week day names will be shown\n     */\n    public final boolean isShowWeekdays() {\n        return showWeekdaysProperty().get();\n    }\n\n    private final BooleanProperty showTimedEntries = new SimpleBooleanProperty(this,\n            \"showTimedEntries\", true);\n\n    /**\n     * Controls whether the view will show calendar entries that are not \"full-day\" entries\n     * (e.g. \"soccer training from 6pm till 8pm\").\n     *\n     * @return true if timed entries will be shown\n     */\n    public final BooleanProperty showTimedEntriesProperty() {\n        return showTimedEntries;\n    }\n\n    /**\n     * Sets the value of {@link #showTimedEntriesProperty()}.\n     *\n     * @param show\n     *            if true timed entries will be shown\n     */\n    public final void setShowTimedEntries(boolean show) {\n        showTimedEntriesProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showTimedEntriesProperty()}.\n     *\n     * @return true if timed entries will be shown\n     */\n    public final boolean isShowTimedEntries() {\n        return showTimedEntriesProperty().get();\n    }\n\n\n    private final BooleanProperty showFullDayEntries = new SimpleBooleanProperty(this,\n            \"showFullDayEntries\", true);\n\n    /**\n     * Controls whether the view will show calendar entries that are \"full-day\" entries\n     * (e.g. \"Birthday Dirk\").\n     *\n     * @return true if full day entries will be shown\n     */\n    public final BooleanProperty showFullDayEntriesProperty() {\n        return showFullDayEntries;\n    }\n\n    /**\n     * Sets the value of {@link #showFullDayEntriesProperty()}.\n     *\n     * @param show\n     *            if true full-day entries will be shown\n     */\n    public final void setShowFullDayEntries(boolean show) {\n        showFullDayEntriesProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showFullDayEntriesProperty()}.\n     *\n     * @return true if full-day entries will be shown\n     */\n    public final boolean isShowFullDayEntries() {\n        return showFullDayEntriesProperty().get();\n    }\n\n    private final ObjectProperty<Callback<Entry<?>, MonthEntryView>> entryViewFactory = new SimpleObjectProperty<>(\n            this, \"entryViewFactory\");\n\n    /**\n     * A factory used for creating instances of type {@link MonthEntryView}.\n     * These views will be shown within each day cell. <h2>Code Example</h2>\n     *\n     * <pre>\n     * setEntryViewFactory(entry -&gt; {\n     * \treturn new MonthEntryView(entry);\n     * });\n     * </pre>\n     *\n     * @return the entry view factory\n     */\n    public final ObjectProperty<Callback<Entry<?>, MonthEntryView>> entryViewFactoryProperty() {\n        return entryViewFactory;\n    }\n\n    /**\n     * Returns the value of {@link #entryViewFactoryProperty()}.\n     *\n     * @return the entry view factory\n     */\n    public final Callback<Entry<?>, MonthEntryView> getEntryViewFactory() {\n        return entryViewFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #entryViewFactoryProperty()}.\n     *\n     * @param factory the entry view factory\n     */\n    public final void setEntryViewFactory(\n            Callback<Entry<?>, MonthEntryView> factory) {\n        requireNonNull(factory);\n        entryViewFactoryProperty().set(factory);\n    }\n\n    @Override\n    public void goBack() {\n        setDate(getDate().minusMonths(1));\n    }\n\n    @Override\n    public void goForward() {\n        setDate(getDate().plusMonths(1));\n    }\n\n    private static final String MONTH_PAGE_CATEGORY = \"Month View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekdaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekdays((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekdays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Weekdays\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the weekdays\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekendsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekends((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekends();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Weekends\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Mark the weekends\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekNumbersProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekNumbers((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekNumbers();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Week Numbers\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the week numbers\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showCurrentWeekProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowCurrentWeek((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowCurrentWeek();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Current Week\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Highlight the current week\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showFullDayEntriesProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowFullDayEntries((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowFullDayEntries();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Full Day Entries\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show full day entries\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTimedEntriesProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowTimedEntries((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowTimedEntries();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Timed Entries\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show timed entries\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_PAGE_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/MonthViewBase.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableSet;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.YearMonth;\nimport java.util.Optional;\n\n/**\n * Common superclass for views showing a month (e.g. {@link MonthView},\n * {@link YearMonthView}).\n */\npublic abstract class MonthViewBase extends DateControl {\n\n    /**\n     * Constructs a new month view.\n     */\n    protected MonthViewBase() {\n        dateProperty().addListener(evt -> yearMonth.set(YearMonth.from(getDate())));\n        selectionModeProperty().addListener(evt -> getSelectedDates().clear());\n    }\n\n    private final ReadOnlyObjectWrapper<YearMonth> yearMonth = new ReadOnlyObjectWrapper<>(this, \"yearMonth\", YearMonth.from(getToday()));\n\n    /**\n     * Stores the year and month shown by the control.\n     *\n     * @return the year and month\n     */\n    public final ReadOnlyObjectProperty<YearMonth> yearMonthProperty() {\n        return yearMonth.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #yearMonthProperty()}.\n     *\n     * @return the year and month\n     */\n    public final YearMonth getYearMonth() {\n        return yearMonthProperty().get();\n    }\n\n    private final BooleanProperty showWeeks = new SimpleBooleanProperty(this,\n            \"showWeeks\", true);\n\n    /**\n     * Controls whether the view will show week numbers. The image below shows\n     * an example (weeks 10, 11, 12 in 2015):\n     *\n     * <img src=\"doc-files/week-numbers.png\" alt=\"Week Numbers\">\n     *\n     *\n     * @return true if week numbers are shown\n     */\n    public final BooleanProperty showWeekNumbersProperty() {\n        return showWeeks;\n    }\n\n    /**\n     * Sets the value of {@link #showWeekNumbersProperty()}.\n     *\n     * @param show\n     *            if true will show week numbers\n     */\n    public final void setShowWeekNumbers(boolean show) {\n        showWeekNumbersProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showWeekNumbersProperty()}.\n     *\n     * @return true if week numbers will be shown\n     */\n    public final boolean isShowWeekNumbers() {\n        return showWeekNumbersProperty().get();\n    }\n\n    private final ObservableSet<LocalDate> selectedDates = FXCollections\n            .observableSet();\n\n    /**\n     * The selected dates.\n     *\n     * @return the selected dates\n     */\n    public final ObservableSet<LocalDate> getSelectedDates() {\n        return selectedDates;\n    }\n\n    private static final String MONTH_VIEW_CATEGORY = \"Month View Base\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showWeekNumbersProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowWeekNumbers((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowWeekNumbers();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Week Numbers\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the week numbers\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/RecurrenceView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\n\nimport impl.com.calendarfx.view.RecurrenceViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.scene.control.Skin;\nimport net.fortuna.ical4j.model.Recur;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeParseException;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A custom control used for editing recurrence rules according to RFC 2445. The\n * image below shows the four configurations of the control depending on the\n * currently selected frequency (daily, weekly, monthly, yearly).\n *\n * <img width=\"80%\" src=\"doc-files/recurrence-view.png\" alt=\"Recurrence Week\">\n *\n */\npublic class RecurrenceView extends CalendarFXControl {\n\n    /**\n     * Constructs a new recurrence view.\n     */\n    public RecurrenceView() {\n        getStyleClass().add(\"recurrence-view\");\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new RecurrenceViewSkin(this);\n    }\n\n    private final ObjectProperty<LocalDate> startDate = new SimpleObjectProperty<>(this, \"startDate\", LocalDate.now()) {\n        @Override\n        public void set(LocalDate newValue) {\n            requireNonNull(newValue);\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * A property used to store the start date for the recurrence rule. The\n     * start date is relevant when creating rules that specify an entry to\n     * occure on a specific day of the month or the week.\n     *\n     * @return the start date\n     */\n    public final ObjectProperty<LocalDate> startDateProperty() {\n        return startDate;\n    }\n\n    /**\n     * Sets the value of {@link #startDateProperty()}.\n     *\n     * @param date\n     *            the new start date\n     */\n    public final void setStartDate(LocalDate date) {\n        requireNonNull(date);\n        startDateProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #startDateProperty()}.\n     *\n     * @return the start date for the rule\n     */\n    public final LocalDate getStartDate() {\n        return startDateProperty().get();\n    }\n\n    private final StringProperty recurrenceRule = new SimpleStringProperty(this, \"recurrenceRule\", \"RRULE:FREQ=DAILY\") {\n        @Override\n        public void set(String newValue) {\n            try {\n                if (newValue != null) {\n                    new Recur(newValue.replaceFirst(\"^RRULE:\", \"\"));\n                }\n                super.set(newValue);\n            } catch (IllegalArgumentException | DateTimeParseException e) {\n                e.printStackTrace();\n            }\n        }\n    };\n\n    /**\n     * Stores the recurrence rule according to RFC 2445 syntax (e.g.\n     * \"RRULE:FREQ=DAILY\").\n     *\n     * @see Entry#recurrenceRuleProperty()\n     *\n     * @return the recurrence rule\n     */\n    public final StringProperty recurrenceRuleProperty() {\n        return recurrenceRule;\n    }\n\n    /**\n     * Sets the value of {@link #recurrenceRuleProperty()}.\n     *\n     * @param rule\n     *            the recurrence rule (e.g. \"RRULE:FREQ=DAILY\").\n     */\n    public final void setRecurrenceRule(String rule) {\n        recurrenceRuleProperty().set(rule);\n    }\n\n    /**\n     * Returns the value of {@link #recurrenceRuleProperty()}.\n     *\n     * @return the recurrence rule (e.g. \"RRULE:FREQ=DAILY\").\n     */\n    public final String getRecurrenceRule() {\n        return recurrenceRuleProperty().get();\n    }\n\n    private final BooleanProperty showSummary = new SimpleBooleanProperty(this, \"showSummary\", true);\n\n    /**\n     * A property used to control the visibility of the \"summary\" label. The\n     * label interprets the recurrence rule and displays it in a human readable\n     * format, e.g. \"Every week on Saturday and Sunday\", or\n     * \"Every 4 days, 5 times\").\n     *\n     * @see Util#convertRFC2445ToText(String, LocalDate)\n     *\n     * @return true if the summary label should be shown\n     */\n    public final BooleanProperty showSummaryProperty() {\n        return showSummary;\n    }\n\n    /**\n     * Returns the value of {@link #showSummaryProperty()}.\n     *\n     * @return true if the summary label will be shown\n     */\n    public final boolean isShowSummary() {\n        return showSummaryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showSummaryProperty()}.\n     *\n     * @param show\n     *            if true the summary label will be shown\n     */\n    public final void setShowSummary(boolean show) {\n        showSummaryProperty().set(show);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/RequestEvent.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.page.DayPage;\nimport com.calendarfx.view.page.YearPage;\nimport javafx.event.Event;\nimport javafx.event.EventTarget;\nimport javafx.event.EventType;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.Year;\nimport java.time.YearMonth;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A specialized event class used by date controls to request that the UI shows\n * a given date, dateTime, month, or year. This event gets, for example, fired\n * when the user double clicks on a day in the {@link YearPage} of the\n * {@link CalendarView}. The {@link CalendarView} will then automatically switch\n * to the {@link DayPage}.\n */\npublic final class RequestEvent extends Event {\n\n    private static final long serialVersionUID = -5343700719205046646L;\n\n    /**\n     * An event type used to request that the UI shows a certain granularity.\n     */\n    public static final EventType<RequestEvent> REQUEST = new EventType<>(\n            RequestEvent.ANY, \"REQUEST\");\n\n    /**\n     * An event type used to request that the UI shows a certain granularity.\n     */\n    public static final EventType<RequestEvent> REQUEST_DATE = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_DATE\");\n\n    /**\n     * An event type used to request that the UI shows a certain granularity.\n     */\n    public static final EventType<RequestEvent> REQUEST_DATE_TIME = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_DATE_TIME\");\n\n    /**\n     * An event type used to request that the UI shows a certain granularity.\n     */\n    public static final EventType<RequestEvent> REQUEST_YEAR_MONTH = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_YEAR_MONTH\");\n\n    /**\n     * An event type used to request that the UI shows a certain granularity.\n     */\n    public static final EventType<RequestEvent> REQUEST_YEAR = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_YEAR\");\n\n    /**\n     * An event type used to request that the UI shows a certain week.\n     */\n    public static final EventType<RequestEvent> REQUEST_WEEK = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_WEEK\");\n\n    /**\n     * An event type used to request that the UI shows a certain entry.\n     */\n    public static final EventType<RequestEvent> REQUEST_ENTRY = new EventType<>(\n            RequestEvent.REQUEST, \"REQUEST_ENTRY\");\n\n    private LocalDate date;\n\n    private LocalDateTime dateTime;\n\n    private YearMonth yearMonth;\n\n    private Year year;\n\n    private Entry<?> entry;\n\n    private int weekOfYear;\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param date\n     *            the day to show\n     */\n    public RequestEvent(Object source, EventTarget target, LocalDate date) {\n        super(source, target, REQUEST_DATE);\n\n        this.date = requireNonNull(date);\n    }\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param dateTime\n     *            the day and dateTime to show\n     */\n    public RequestEvent(Object source, EventTarget target, LocalDateTime dateTime) {\n        super(source, target, REQUEST_DATE_TIME);\n\n        this.dateTime = requireNonNull(dateTime);\n    }\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param year\n     *            the year\n     * @param weekOfYear\n     *            the week of year number\n     */\n    public RequestEvent(Object source, EventTarget target, Year year, int weekOfYear) {\n        super(source, target, REQUEST_WEEK);\n\n        this.year = requireNonNull(year);\n        this.weekOfYear = weekOfYear;\n    }\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param yearMonth\n     *            the year month to show\n     */\n    public RequestEvent(Object source, EventTarget target, YearMonth yearMonth) {\n        super(source, target, REQUEST_YEAR_MONTH);\n\n        this.yearMonth = requireNonNull(yearMonth);\n    }\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param year\n     *            the year to show\n     */\n    public RequestEvent(Object source, EventTarget target, Year year) {\n        super(source, target, REQUEST_YEAR);\n\n        this.year = requireNonNull(year);\n    }\n\n    /**\n     * Constructs a new request event.\n     *\n     * @param source\n     *            the event source\n     * @param target\n     *            the event target\n     * @param entry\n     *            the entry to show\n     */\n    public RequestEvent(Object source, EventTarget target, Entry<?> entry) {\n        super(source, target, REQUEST_ENTRY);\n\n        this.entry = requireNonNull(entry);\n    }\n\n    /**\n     * Returns the requested date.\n     *\n     * @return the date\n     */\n    public LocalDate getDate() {\n        return date;\n    }\n\n    /**\n     * Returns the requested date and time.\n     *\n     * @return the date and time\n     */\n    public LocalDateTime getDateTime() {\n        return dateTime;\n    }\n\n    /**\n     * Returns the requested year and month.\n     *\n     * @return the requested year and month\n     */\n    public YearMonth getYearMonth() {\n        return yearMonth;\n    }\n\n    /**\n     * Returns the requested year.\n     *\n     * @return the requested year\n     */\n    public Year getYear() {\n        return year;\n    }\n\n    /**\n     * Returns the requested entry.\n     *\n     * @return the requested entry\n     */\n    public Entry<?> getEntry() {\n        return entry;\n    }\n\n    /**\n     * Returns the requested week of year.\n     *\n     * @return the requested week of year\n     */\n    public int getWeekOfYear() {\n        return weekOfYear;\n    }\n\n    @Override\n    public String toString() {\n        return \"RequestEvent [date=\" + date + \", dateTime=\" + dateTime\n                + \", yearMonth=\" + yearMonth + \", year=\" + year + \", entry=\"\n                + entry + \", weekOfYear\" + weekOfYear + \", eventType=\" + eventType + \", target=\" + target\n                + \", consumed=\" + consumed + \", source=\" + source + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/ResourceCalendarView.java",
    "content": "package com.calendarfx.view;\n\nimport com.calendarfx.model.Marker;\nimport impl.com.calendarfx.view.ResourceCalendarViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyMapProperty;\nimport javafx.beans.property.ReadOnlyMapWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\n\npublic class ResourceCalendarView<T> extends DayViewBase {\n\n    public ResourceCalendarView() {\n        getStyleClass().add(\"resource-calendar-view\");\n\n        setEnableCurrentTimeMarker(false);\n        setScrollingEnabled(true);\n        setHourHeight(40);\n        setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_HEIGHT);\n\n        ListChangeListener<? super T> l = change -> {\n            while (change.next()) {\n                if (change.wasAdded()) {\n                    change.getAddedSubList().forEach(resource -> {\n                        DayView dayView = new DayView();\n                        bind(dayView, true);\n                        partialUnbinding(dayView);\n                        dayViewMap.put(resource, dayView);\n                    });\n                } else if (change.wasRemoved()) {\n                    change.getRemoved().forEach(resource -> dayViewMap.remove(resource));\n                }\n            }\n        };\n\n        resources.addListener(l);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new ResourceCalendarViewSkin(this);\n    }\n\n    private final BooleanProperty showTimeScale = new SimpleBooleanProperty(this, \"showTimeScale\", true);\n\n    /**\n     * Controls whether the timescale should be shown on the left-hand side or not.\n     *\n     * @return true if the scale should be shown\n     */\n    public BooleanProperty showTimeScaleProperty() {\n        return showTimeScale;\n    }\n\n    public boolean isShowTimeScale() {\n        return showTimeScale.get();\n    }\n\n    public void setShowTimeScale(boolean showTimeScale) {\n        this.showTimeScale.set(showTimeScale);\n    }\n\n    private final ObservableList<Marker> markers = FXCollections.observableArrayList();\n\n    /**\n     * A list of marker lines that can be added to the view.\n     *\n     * @return visible horizontal markers\n     */\n    public final ObservableList<Marker> getMarkers() {\n        return markers;\n    }\n\n    private void partialUnbinding(DayView otherControl) {\n        Bindings.unbindBidirectional(otherControl.draggedEntryProperty(), draggedEntryProperty());\n        Bindings.unbindContentBidirectional(otherControl.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        Bindings.unbindContentBidirectional(otherControl.getCalendarSources(), getCalendarSources());\n    }\n\n    private final BooleanProperty showScrollBar = new SimpleBooleanProperty(this, \"showScrollBar\", true);\n\n    public final boolean isShowScrollBar() {\n        return showScrollBar.get();\n    }\n\n    public final BooleanProperty showScrollBarProperty() {\n        return showScrollBar;\n    }\n\n    public final void setShowScrollBar(boolean showScrollBar) {\n        this.showScrollBar.set(showScrollBar);\n    }\n\n    private final ObjectProperty<Callback<T, Node>> headerFactory = new SimpleObjectProperty<>(this, \"headerFactory\", it -> new Label(\"Header\"));\n\n    public final Callback<T, Node> getHeaderFactory() {\n        return headerFactory.get();\n    }\n\n    public final ObjectProperty<Callback<T, Node>> headerFactoryProperty() {\n        return headerFactory;\n    }\n\n    public final void setHeaderFactory(Callback<T, Node> headerFactory) {\n        this.headerFactory.set(headerFactory);\n    }\n\n    private final ReadOnlyMapProperty<T, DayView> dayViewMap = new ReadOnlyMapWrapper<>(this, \"dayViewMap\", FXCollections.observableHashMap());\n\n    public final ObservableMap getDayViewMap() {\n        return dayViewMap.get();\n    }\n\n    public final ReadOnlyMapProperty<T, DayView> dayViewMapProperty() {\n        return dayViewMap;\n    }\n\n    public final DayView getDayView(T resource) {\n        return dayViewMap.get(resource);\n    }\n\n    private final ObservableList<T> resources = FXCollections.observableArrayList();\n\n    public final ObservableList<T> getResources() {\n        return resources;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/ResourcesView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Resource;\nimport impl.com.calendarfx.view.ResourcesViewSkin;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\n\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.function.Consumer;\n\nimport static com.calendarfx.view.RequestEvent.REQUEST_ENTRY;\n\n/**\n * The detailed day view is a composite control consisting of a {@link DayView},\n * an {@link AllDayView}, an {@link CalendarHeaderView}, and a\n * {@link TimeScaleView}. The image below shows the standard appearance of the\n * view. The second image shows the same view with the optional agenda view made\n * visible.\n *\n * <img src=\"doc-files/detailed-day-view.png\" alt=\"Detailed Day View\">\n * <img src=\"doc-files/detailed-day-view-agenda.png\" alt=\"Detailed Day View Agenda\">\n */\npublic class ResourcesView<T extends Resource<?>> extends DayViewBase {\n\n    private static final String DEFAULT_STYLE = \"resources-view\";\n\n\n    /**\n     * Constructs a new day view.\n     */\n    public ResourcesView() {\n        getStyleClass().add(DEFAULT_STYLE);\n        setShowToday(false);\n        setGridType(GridType.CUSTOM);\n\n        // calling \"editEntry\" with \"false\" flag because we do not want to change the start date of the view\n        addEventHandler(REQUEST_ENTRY, evt -> maybeRunAndConsume(evt, e -> editEntry(evt.getEntry(), false)));\n\n        VirtualGrid grid = new VirtualGrid(\"Editing Grid\", \"Editing Grid\", ChronoUnit.MINUTES, 20);\n\n        setVirtualGrid(grid);\n        setAvailabilityGrid(grid);\n        setGridLines(grid);\n\n        Label weekNumberLabel = new Label();\n        weekNumberLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        weekNumberLabel.getStyleClass().add(\"week-number-label\");\n        weekNumberLabel.setAlignment(Pos.CENTER);\n        weekNumberLabel.textProperty().bind(Bindings.createStringBinding(() -> DateTimeFormatter.ofPattern(\"w\").format(getDate()), dateProperty()));\n        setUpperLeftCorner(weekNumberLabel);\n\n        InvalidationListener adjustListener = it -> maybeAdjustToFirstDayOfWeek();\n        dateProperty().addListener(adjustListener);\n        adjustToFirstDayOfWeekProperty().addListener(adjustListener);\n        maybeAdjustToFirstDayOfWeek();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new ResourcesViewSkin(this);\n    }\n\n    private void maybeAdjustToFirstDayOfWeek() {\n        if (isAdjustToFirstDayOfWeek()) {\n            setDate(getDate().with(TemporalAdjusters.previousOrSame(getFirstDayOfWeek())));\n        }\n    }\n\n    /**\n     * A list of possible visualization types that the resources view can support.\n     */\n    public enum Type {\n        /**\n         * Display the resources at the very top and then one or more days below each\n         * resource.\n         */\n        RESOURCES_OVER_DATES,\n\n        /**\n         * Display the dates at the very top and then one or more resources below each\n         * date.\n         */\n        DATES_OVER_RESOURCES\n    }\n\n    private final ObjectProperty<Type> type = new SimpleObjectProperty<>(this, \"type\", Type.RESOURCES_OVER_DATES);\n\n    public final Type getType() {\n        return type.get();\n    }\n\n    /**\n     * Determines the visualization type: resoruces over dates or dates over resources.\n     *\n     * @return the visualization type\n     */\n    public final ObjectProperty<Type> typeProperty() {\n        return type;\n    }\n\n    public final void setType(Type type) {\n        this.type.set(type);\n    }\n\n    private void maybeRunAndConsume(RequestEvent evt, Consumer<RequestEvent> consumer) {\n        if (!evt.isConsumed()) {\n            consumer.accept(evt);\n            evt.consume();\n        }\n    }\n\n    private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, \"adjustToFirstDayOfWeek\", true);\n\n    /**\n     * A flag used to indicate that the view should always show the first day of\n     * the week (e.g. \"Monday\") at its beginning even if the\n     * {@link #dateProperty()} is set to another day (e.g. \"Thursday\").\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final BooleanProperty adjustToFirstDayOfWeekProperty() {\n        return adjustToFirstDayOfWeek;\n    }\n\n    /**\n     * Returns the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final boolean isAdjustToFirstDayOfWeek() {\n        return adjustToFirstDayOfWeekProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @param adjust if true the view will always show the first day of the week\n     */\n    public final void setAdjustToFirstDayOfWeek(boolean adjust) {\n        adjustToFirstDayOfWeekProperty().set(adjust);\n    }\n\n    private final ObjectProperty<Callback<T, AllDayView>> allDayViewFactory = new SimpleObjectProperty<>(this, \"allDayViewFactory\", it -> new AllDayView(getNumberOfDays()));\n\n    public final Callback<T, AllDayView> getAllDayViewFactory() {\n        return allDayViewFactory.get();\n    }\n\n    /**\n     * A factory used for creating a new {@link AllDayView} instance for each resource\n     * shown in the view.\n     *\n     * @return a factory for all day views\n     */\n    public final ObjectProperty<Callback<T, AllDayView>> allDayViewFactoryProperty() {\n        return allDayViewFactory;\n    }\n\n    public final void setAllDayViewFactory(Callback<T, AllDayView> allDayViewFactory) {\n        this.allDayViewFactory.set(allDayViewFactory);\n    }\n\n    private final ObjectProperty<Callback<T, WeekDayHeaderView>> weekDayHeaderViewFactory = new SimpleObjectProperty<>(this, \"weekDayHeaderViewFactory\", it -> new WeekDayHeaderView(getNumberOfDays()));\n\n    public final Callback<T, WeekDayHeaderView> getWeekDayHeaderViewFactory() {\n        return weekDayHeaderViewFactory.get();\n    }\n\n    /**\n     * A factory used for creating a new {@link WeekDayHeaderView} instance for each resource\n     * shown in the view.\n     *\n     * @return a factory for week day header views\n     */\n    public final ObjectProperty<Callback<T, WeekDayHeaderView>> weekDayHeaderViewFactoryProperty() {\n        return weekDayHeaderViewFactory;\n    }\n\n    public final void setWeekDayHeaderViewFactory(Callback<T, WeekDayHeaderView> factory) {\n        this.weekDayHeaderViewFactory.set(factory);\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\", 7);\n\n    /**\n     * Stores the number of days that will be shown by this view.\n     *\n     * @return the number of days shown by the view\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \" + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n\n    private final ObjectProperty<Callback<T, Node>> resourceHeaderFactory = new SimpleObjectProperty<>(this, \"headerFactory\", resource -> {\n        Label label = new Label(resource.toString());\n        label.setAlignment(Pos.CENTER);\n        label.getStyleClass().add(\"resource-header\");\n        label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        return label;\n    });\n\n    public final Callback<T, Node> getResourceHeaderFactory() {\n        return resourceHeaderFactory.get();\n    }\n\n    /**\n     * A factory callback for creating a header for each resource\n     *\n     * @return the resource header factory callback\n     */\n    public final ObjectProperty<Callback<T, Node>> resourceHeaderFactoryProperty() {\n        return resourceHeaderFactory;\n    }\n\n    public final void setResourceHeaderFactory(Callback<T, Node> resourceHeaderFactory) {\n        this.resourceHeaderFactory.set(resourceHeaderFactory);\n    }\n\n    private final ObservableList<T> resources = FXCollections.observableArrayList();\n\n    /**\n     * The resources to be shown in this view.\n     *\n     * @return the list of resources\n     */\n    public final ObservableList<T> getResources() {\n        return resources;\n    }\n\n    // show all day view support\n\n    private final BooleanProperty showAllDayView = new SimpleBooleanProperty(this, \"showAllDayView\", true);\n\n    /**\n     * A property used to toggle the visibility of the all day view.\n     *\n     * @return true if the all day view will be visible\n     */\n    public final BooleanProperty showAllDayViewProperty() {\n        return showAllDayView;\n    }\n\n    /**\n     * Sets the value of {@link #showAllDayViewProperty()}.\n     *\n     * @return true if the all day view will be visible\n     */\n    public final boolean isShowAllDayView() {\n        return showAllDayViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showAllDayViewProperty()}.\n     *\n     * @param show true if the all day view will be visible\n     */\n    public final void setShowAllDayView(boolean show) {\n        showAllDayViewProperty().set(show);\n    }\n\n    // show timescale view support\n\n    private final BooleanProperty showTimeScaleView = new SimpleBooleanProperty(this, \"showTimeScaleView\", true);\n\n    /**\n     * A property used to toggle the visibility of the time scale view.\n     *\n     * @return true if the timescale view will be visible\n     */\n    public final BooleanProperty showTimeScaleViewProperty() {\n        return showTimeScaleView;\n    }\n\n    /**\n     * Returns the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @return true if the timescale view will be visible\n     */\n    public final boolean isShowTimeScaleView() {\n        return showTimeScaleViewProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showTimeScaleViewProperty()}.\n     *\n     * @param show if true the timescale view will be visible\n     */\n    public final void setShowTimeScaleView(boolean show) {\n        showTimeScaleViewProperty().set(show);\n    }\n\n    // show scrollbar support\n\n    private final BooleanProperty showScrollBar = new SimpleBooleanProperty(this, \"showScrollBar\", true);\n\n    /**\n     * A property used to control the visibility of the vertial scrollbar.\n     *\n     * @return true if the scrollbar should be shown to the user\n     */\n    public final BooleanProperty showScrollBarProperty() {\n        return showScrollBar;\n    }\n\n    /**\n     * Sets the value of {@link #showScrollBarProperty()}.\n     *\n     * @param showScrollBar if true the scrollbar will be visible\n     */\n    public final void setShowScrollBar(boolean showScrollBar) {\n        this.showScrollBar.set(showScrollBar);\n    }\n\n    /**\n     * Returns the value of {@link #showScrollBarProperty()}.\n     *\n     * @return true if the scrollbar will be visible\n     */\n    public final boolean isShowScrollBar() {\n        return showScrollBar.get();\n    }\n\n    private final ObjectProperty<Callback<T, WeekView>> weekViewFactory = new SimpleObjectProperty<>(this, \"weekViewFactory\", resource -> new WeekView(getNumberOfDays()));\n\n    public final Callback<T, WeekView> getWeekViewFactory() {\n        return weekViewFactory.get();\n    }\n\n    /**\n     * A factory used for creating a new {@link WeekView} instance for each resource\n     * shown in the view.\n     *\n     * @return a factory for resource week views\n     */\n    public final ObjectProperty<Callback<T, WeekView>> weekViewFactoryProperty() {\n        return weekViewFactory;\n    }\n\n    public void setWeekViewFactory(Callback<T, WeekView> weekViewFactory) {\n        this.weekViewFactory.set(weekViewFactory);\n    }\n\n    private final ObjectProperty<Callback<T, DayView>> dayViewFactory = new SimpleObjectProperty<>(this, \"dayViewFactory\", resource -> new DayView());\n\n    public final Callback<T, DayView> getDayViewFactory() {\n        return dayViewFactory.get();\n    }\n\n    /**\n     * A factory used for creating a new {@link DayView} instance for a resource day\n     * shown in the view.\n     *\n     * @return a factory for resource day views\n     */\n    public final ObjectProperty<Callback<T, DayView>> dayViewFactoryProperty() {\n        return dayViewFactory;\n    }\n\n    public void setDayViewFactory(Callback<T, DayView> dayViewFactory) {\n        this.dayViewFactory.set(dayViewFactory);\n    }\n\n    private final ObjectProperty<Node> upperLeftCorner = new SimpleObjectProperty<>(this, \"upperLeftCorner\");\n\n    public final Node getUpperLeftCorner() {\n        return upperLeftCorner.get();\n    }\n\n    /**\n     * Specifies a node that will be placed in the upper left corner of the view.\n     *\n     * @return the upper left corner node\n     */\n    public final ObjectProperty<Node> upperLeftCornerProperty() {\n        return upperLeftCorner;\n    }\n\n    public final void setUpperLeftCorner(Node upperLeftCorner) {\n        this.upperLeftCorner.set(upperLeftCorner);\n    }\n\n    private final ObjectProperty<Node> upperRightCorner = new SimpleObjectProperty<>(this, \"upperRightCorner\", new Region());\n\n    public final Node getUpperRightCorner() {\n        return upperRightCorner.get();\n    }\n\n    /**\n     * Specifies a node that will be placed in the upper right corner of the view.\n     *\n     * @return the upper right corner node\n     */\n    public final ObjectProperty<Node> upperRightCornerProperty() {\n        return upperRightCorner;\n    }\n\n    public final void setUpperRightCorner(Node upperRightCorner) {\n        this.upperRightCorner.set(upperRightCorner);\n    }\n\n    private final ObjectProperty<Callback<ResourcesView<T>, Region>> smallSeparatorFactory = new SimpleObjectProperty<>(this, \"smallSeparatorFactory\", it -> {\n        Region region = new Region();\n        region.getStyleClass().add(\"small-separator\");\n        return region;\n    });\n\n    public final Callback<ResourcesView<T>, Region> getSmallSeparatorFactory() {\n        return smallSeparatorFactory.get();\n    }\n\n    public final ObjectProperty<Callback<ResourcesView<T>, Region>> smallSeparatorFactoryProperty() {\n        return smallSeparatorFactory;\n    }\n\n    public final void setSmallSeparatorFactory(Callback<ResourcesView<T>, Region> smallSeparatorFactory) {\n        this.smallSeparatorFactory.set(smallSeparatorFactory);\n    }\n\n    private final ObjectProperty<Callback<ResourcesView<T>, Region>> largeSeparatorFactory = new SimpleObjectProperty<>(this, \"largeSeparatorFactory\", it -> {\n        Region region = new Region();\n        region.getStyleClass().add(\"large-separator\");\n        return region;\n    });\n\n\n    public final Callback<ResourcesView<T>, Region> getLargeSeparatorFactory() {\n        return largeSeparatorFactory.get();\n    }\n\n    /**\n     * A factory used for creating the vertical separators between the resources.\n     *\n     * @return the resource separator factory\n     */\n    public final ObjectProperty<Callback<ResourcesView<T>, Region>> largeSeparatorFactoryProperty() {\n        return largeSeparatorFactory;\n    }\n\n    public final void setLargeSeparatorFactory(Callback<ResourcesView<T>, Region> largeSeparatorFactory) {\n        this.largeSeparatorFactory.set(largeSeparatorFactory);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/SearchResultView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport impl.com.calendarfx.view.SearchResultViewSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.MapChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.concurrent.Service;\nimport javafx.concurrent.Task;\nimport javafx.scene.control.Skin;\n\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static com.calendarfx.util.LoggingDomain.SEARCH;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.logging.Level.FINE;\n\n/**\n * A view used for searching entries in the calendars and for displaying the\n * results.\n *\n * <img src=\"doc-files/search-result-view.png\" alt=\"Search Result View\">\n *\n * To perform a search the application simply needs to change the value of the\n * text property of this view. The easiest way to do this is to bind the\n * property to the text property of a textfield.\n *\n * @see Calendar#findEntries(String)\n * @see Entry#matches(String)\n */\npublic class SearchResultView extends CalendarFXControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"search-result-view\";\n\n    private static final String SELECTED_ENTRY = \"selected.search.result\";\n\n    private final SearchService searchService;\n\n    /**\n     * Constructs a new view.\n     */\n    public SearchResultView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        searchService = new SearchService();\n        searchService.setOnSucceeded(evt -> updateSearchResults());\n\n        searchTextProperty().addListener(it -> {\n            if (SEARCH.isLoggable(FINE)) {\n                SEARCH.fine(\"restarting search service\");\n            }\n\n            searchService.restart();\n        });\n\n        /*\n         * Listens to changes to the properties map. Each control has a\n         * properties map associated with it. We are using the map to pass\n         * values from the skin to the control. This allows the skin to update\n         * read-only properties.\n         */\n        MapChangeListener<? super Object, ? super Object> listener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(SELECTED_ENTRY)) {\n                    Entry<?> entry = (Entry<?>) change.getValueAdded();\n                    selectedEntry.set(entry);\n                    getProperties().remove(SELECTED_ENTRY);\n                }\n            }\n        };\n\n        getProperties().addListener(listener);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new SearchResultViewSkin(this);\n    }\n\n    private final ObservableList<Entry<?>> searchResults = FXCollections.observableArrayList();\n\n    /**\n     * The list containing the search results.\n     *\n     * @return the search results\n     */\n    public final ObservableList<Entry<?>> getSearchResults() {\n        return searchResults;\n    }\n\n    private void updateSearchResults() {\n        List<Entry<?>> searchResult = searchService.getValue();\n        getSearchResults().setAll(searchResult);\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections\n            .observableArrayList();\n\n    /**\n     * The list of calendar sources where the view will perform the search.\n     *\n     * @return the calendar sources\n     */\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ReadOnlyObjectWrapper<Entry<?>> selectedEntry = new ReadOnlyObjectWrapper<>(\n            this, \"selectedEntry\");\n\n    /**\n     * Stores the currently selected entry / search result.\n     *\n     * @return the selected result\n     */\n    public final ReadOnlyObjectProperty<Entry<?>> selectedEntryProperty() {\n        return this.selectedEntry.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #selectedEntryProperty()}.\n     *\n     * @return the selected entry / search result\n     */\n    public final Entry<?> getSelectedEntry() {\n        return this.selectedEntryProperty().get();\n    }\n\n    private final ObjectProperty<ZoneId> zoneId = new SimpleObjectProperty<>(\n            this, \"zoneId\", ZoneId.systemDefault());\n\n    /**\n     * Provides the current time zone. The zone is required for searches as the\n     * view will invoke {@link Calendar#findEntries(String)} where the\n     * time zone is a required parameter.\n     *\n     * @return the time zone\n     */\n    public final ObjectProperty<ZoneId> zoneIdProperty() {\n        return zoneId;\n    }\n\n    /**\n     * Sets the value of {@link #zoneIdProperty()}.\n     *\n     * @param zoneId\n     *            the time zone\n     */\n    public final void setZoneId(ZoneId zoneId) {\n        requireNonNull(zoneId);\n        zoneIdProperty().set(zoneId);\n    }\n\n    /**\n     * Returns the value of {@link #zoneIdProperty()}.\n     *\n     * @return the time zone\n     */\n    public final ZoneId getZoneId() {\n        return zoneIdProperty().get();\n    }\n\n    private final StringProperty searchText = new SimpleStringProperty(this,\n            \"searchText\");\n\n    /**\n     * The text used to perform the search in the calendars.\n     *\n     * @see Calendar#findEntries(String)\n     * @see Entry#matches(String)\n     *\n     * @return the search term\n     */\n    public final StringProperty searchTextProperty() {\n        return searchText;\n    }\n\n    /**\n     * Returns the value of {@link #searchTextProperty()}.\n     *\n     * @return the search text\n     */\n    public final String getSearchText() {\n        return searchTextProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #searchTextProperty()}.\n     *\n     * @param text\n     *            the search text\n     */\n    public final void setSearchText(String text) {\n        searchTextProperty().set(text);\n    }\n\n    private class SearchService extends Service<List<Entry<?>>> {\n\n        public SearchService() {\n        }\n\n        @Override\n        protected Task<List<Entry<?>>> createTask() {\n            return new SearchTask();\n        }\n\n        class SearchTask extends Task<List<Entry<?>>> {\n\n            @Override\n            protected List<Entry<?>> call() throws Exception {\n                if (!isCancelled()) {\n\n                    String searchText = getSearchText();\n\n                    if (SEARCH.isLoggable(FINE)) {\n                        SEARCH.fine(\"search text: \" + searchText);\n                    }\n\n                    if (searchText == null || searchText.trim().isEmpty()) {\n                        return Collections.emptyList();\n                    }\n\n                    /*\n                     * Let's sleep a little bit, so we don't run a query after\n                     * every key press event.\n                     */\n                    Thread.sleep(200);\n\n                    if (SEARCH.isLoggable(FINE)) {\n                        SEARCH.fine(\"performing search after delay\");\n                    }\n\n                    if (!isCancelled()) {\n\n                        List<Entry<?>> totalResult = new ArrayList<>();\n\n                        for (CalendarSource source : getCalendarSources()) {\n\n                            if (SEARCH.isLoggable(FINE)) {\n                                SEARCH.fine(\"searching in source \"\n                                        + source.getName());\n                            }\n\n                            for (Calendar calendar : source.getCalendars()) {\n\n                                if (SEARCH.isLoggable(FINE)) {\n                                    SEARCH.fine(\"searching in calendar \"\n                                            + calendar.getName());\n                                }\n\n                                if (!isCancelled()) {\n                                    try {\n                                        List<? extends Entry<?>> result = calendar\n                                                .findEntries(searchText);\n                                        if (result != null) {\n                                            for (Entry<?> entry : result) {\n                                                totalResult.add(entry);\n                                            }\n                                        }\n                                    } catch (Exception e) {\n                                        e.printStackTrace();\n                                    }\n                                }\n                            }\n                        }\n\n                        if (SEARCH.isLoggable(FINE)) {\n                            if (isCancelled()) {\n                                SEARCH.fine(\"search was canceled\");\n                            }\n                        }\n\n                        if (SEARCH.isLoggable(FINE)) {\n                            SEARCH.fine(\n                                    \"found \" + totalResult.size() + \" entries\");\n                        }\n\n                        return totalResult;\n                    }\n                }\n\n                if (SEARCH.isLoggable(FINE)) {\n                    SEARCH.fine(\"returning empty search result\");\n                }\n\n                return Collections.emptyList();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/SourceGridView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport impl.com.calendarfx.view.SourceGridViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.util.Optional;\n\npublic class SourceGridView extends CalendarFXControl {\n\n    private static final int DEFAULT_MAXIMUM_ROWS_PER_COLUMN = 5;\n\n    public SourceGridView() {\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new SourceGridViewSkin(this);\n    }\n\n    public final void bind(DateControl dateControl) {\n        Bindings.bindContentBidirectional(calendarSources, dateControl.getCalendarSources());\n        Bindings.bindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n    }\n\n    public final void unbind(DateControl dateControl) {\n        Bindings.unbindContentBidirectional(calendarSources, dateControl.getCalendarSources());\n        Bindings.unbindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n\n        // important, otherwise we end up with a memory leak\n        dateControl.getCalendarVisibilityMap().clear();\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections.observableArrayList();\n\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ObservableMap<Calendar, BooleanProperty> calendarVisibilityMap = FXCollections.observableHashMap();\n\n    public final ObservableMap<Calendar, BooleanProperty> getCalendarVisibilityMap() {\n        return calendarVisibilityMap;\n    }\n\n    public final BooleanProperty getCalendarVisibilityProperty(Calendar calendar) {\n        return calendarVisibilityMap.computeIfAbsent(calendar, cal -> new SimpleBooleanProperty(SourceGridView.this, \"visible\", true));\n    }\n\n    public final boolean isCalendarVisible(Calendar calendar) {\n        BooleanProperty prop = getCalendarVisibilityProperty(calendar);\n        return prop.get();\n    }\n\n    public final void setCalendarVisibility(Calendar calendar, boolean visible) {\n        BooleanProperty prop = getCalendarVisibilityProperty(calendar);\n        prop.set(visible);\n    }\n\n    private final IntegerProperty maximumRowsPerColumn = new SimpleIntegerProperty(this, \"maximumRowsPerColumn\", DEFAULT_MAXIMUM_ROWS_PER_COLUMN) {\n        @Override\n        public void set(int newValue) {\n            if (newValue < 1) {\n                throw new RuntimeException(\"Invalid Number! \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    public final IntegerProperty maximumRowsPerColumnProperty() {\n        return maximumRowsPerColumn;\n    }\n\n    public final int getMaximumRowsPerColumn() {\n        return maximumRowsPerColumnProperty().get();\n    }\n\n\n    public final void setMaximumRowsPerColumn(final int maximumRowsPerColumn) {\n        maximumRowsPerColumnProperty().set(maximumRowsPerColumn);\n    }\n\n    private static final String SOURCE_GRID_VIEW_CATEGORY = \"Source Grid View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n            @Override\n            public void setValue(Object value) {\n                setMaximumRowsPerColumn((int) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getMaximumRowsPerColumn();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(maximumRowsPerColumnProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Maximum Rows Per Column\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"The maximum number of rows per column\";\n            }\n\n            @Override\n            public String getCategory() {\n                return SOURCE_GRID_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/SourceView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport impl.com.calendarfx.view.SourceViewSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.Skin;\n\n/**\n * A view specialized in showing a list of {@link CalendarSource} instances.\n * This list can be used to toggle the visibility of calendars.\n *\n * <img src=\"doc-files/source-view.png\" alt=\"Source View\">\n */\npublic class SourceView extends CalendarFXControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"source-view\";\n\n    /**\n     * Constructs a new source view.\n     */\n    public SourceView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n        createContextMenu();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new SourceViewSkin(this);\n    }\n\n    public final void bind(DateControl dateControl) {\n        Bindings.bindContentBidirectional(calendarSources, dateControl.getCalendarSources());\n        Bindings.bindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n    }\n\n    public final void unbind(DateControl dateControl) {\n        Bindings.unbindContentBidirectional(calendarSources, dateControl.getCalendarSources());\n        Bindings.unbindContentBidirectional(calendarVisibilityMap, dateControl.getCalendarVisibilityMap());\n\n        // important, otherwise we end up with a memory leak\n        dateControl.getCalendarVisibilityMap().clear();\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections.observableArrayList();\n\n    /**\n     * The list of calendar sources shown by the view.\n     *\n     * @return the calendar sources\n     */\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ObservableMap<Calendar, BooleanProperty> calendarVisibilityMap = FXCollections.observableHashMap();\n\n    public final ObservableMap<Calendar, BooleanProperty> getCalendarVisibilityMap() {\n        return calendarVisibilityMap;\n    }\n\n    public final BooleanProperty getCalendarVisibilityProperty(Calendar calendar) {\n        return calendarVisibilityMap.computeIfAbsent(calendar, cal -> new SimpleBooleanProperty(SourceView.this, \"visible\", true));\n    }\n\n    public final boolean isCalendarVisible(Calendar calendar) {\n        return getCalendarVisibilityProperty(calendar).get();\n    }\n\n    public final void setCalendarVisibility(Calendar calendar, boolean visible) {\n        getCalendarVisibilityProperty(calendar).set(visible);\n    }\n\n    private void createContextMenu() {\n        MenuItem disableAll = new MenuItem();\n        disableAll.setText(Messages.getString(\"SourceView.DISABLE_ALL\"));\n        disableAll.setOnAction(evt -> {\n            for (CalendarSource source : getCalendarSources()) {\n                for (Calendar calendar : source.getCalendars()) {\n                    setCalendarVisibility(calendar, false);\n                }\n            }\n        });\n\n        MenuItem enableAll = new MenuItem();\n        enableAll.setText(Messages.getString(\"SourceView.ENABLE_ALL\"));\n        enableAll.setOnAction(evt -> {\n            for (CalendarSource source : getCalendarSources()) {\n                for (Calendar calendar : source.getCalendars()) {\n                    setCalendarVisibility(calendar, true);\n                }\n            }\n        });\n\n        ContextMenu contextMenu = new ContextMenu();\n        contextMenu.getItems().addAll(disableAll, enableAll);\n        setContextMenu(contextMenu);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/TimeField.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.TimeFieldSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalTime;\nimport java.util.Optional;\n\n/**\n * A control for editing a {@link LocalTime}.\n *\n * <img src=\"doc-files/time-field.png\" alt=\"Time Field\">\n *\n */\npublic class TimeField extends CalendarFXControl {\n\n    /**\n     * Constructs a new field.\n     */\n    public TimeField() {\n        getStyleClass().add(\"time-field\");\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new TimeFieldSkin(this);\n    }\n\n    private final BooleanProperty rollOver = new SimpleBooleanProperty(this, \"rollOver\", true);\n\n    /**\n     * Determines if the field for hours will jump from 23 to 0 when the user\n     * increases the hour value (and vice versa when decreasing). Same for\n     * minutes.\n     *\n     * @return the roll over flag\n     */\n    public final BooleanProperty rollOverProperty() {\n        return rollOver;\n    }\n\n    /**\n     * Returns the value of {@link #rollOverProperty()}.\n     *\n     * @return true if the fields will roll over\n     */\n    public final boolean isRollOver() {\n        return rollOver.get();\n    }\n\n    /**\n     * Sets the value of {@link #rollOverProperty()}.\n     *\n     * @param roll if true the fields will roll over (e.g. from 23 to 0 or from 59 to 0).\n     */\n    public final void setRollOver(boolean roll) {\n        rollOver.set(roll);\n    }\n\n    private final ObjectProperty<LocalTime> value = new SimpleObjectProperty<>(this, \"value\", LocalTime.now());\n\n    /**\n     * The current local time value of the field.\n     *\n     * @return the local time value\n     */\n    public final ObjectProperty<LocalTime> valueProperty() {\n        return this.value;\n    }\n\n    /**\n     * Returns the value of {@link #valueProperty()}.\n     *\n     * @return the local time value\n     */\n    public final LocalTime getValue() {\n        return valueProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #valueProperty()}.\n     *\n     * @param localTime the new time\n     */\n    public final void setValue(LocalTime localTime) {\n        valueProperty().set(localTime);\n    }\n\n    private static final String TIME_FIELD_CATEGORY = \"Time Field\";\n\n    /**\n     * Returns a list of property items that can be shown by the\n     * {@link PropertySheet} of ControlsFX.\n     *\n     * @return the property sheet items\n     */\n    public ObservableList<Item> getPropertySheetItems() {\n\n        ObservableList<Item> items = FXCollections.observableArrayList();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(rollOverProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setRollOver((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isRollOver();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Roll Over\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Roll Over\";\n            }\n\n            @Override\n            public String getCategory() {\n                return TIME_FIELD_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(valueProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                TimeField.this.setValue((LocalTime) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return TimeField.this.getValue();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalTime.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Value\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Local Time\";\n            }\n\n            @Override\n            public String getCategory() {\n                return TIME_FIELD_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/TimeScaleView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.TimeScaleViewSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.function.Function;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A control used for displaying a vertical time scale.\n *\n * <img src=\"doc-files/time-scale-view.png\" alt=\"Time Scale View\">\n */\npublic class TimeScaleView extends DayViewBase {\n\n    /**\n     * Constructs a new scale view.\n     */\n    public TimeScaleView() {\n        getStyleClass().add(\"time-scale\");\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new TimeScaleViewSkin<>(this);\n    }\n\n    // DATE FORMATTER\n\n    private final ObjectProperty<DateTimeFormatter> dateFormatter = new SimpleObjectProperty<>(this, \"dateFormatter\", DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));\n\n    /**\n     * Gets the DateTimeFormatter instance, which is used to provide the format\n     * on the TimeScale for those labels that are displaying a date.\n     * By default it has a value of {@link FormatStyle#SHORT}.\n     *\n     * @return the date formatter.\n     */\n    public final ObjectProperty<DateTimeFormatter> dateFormatterProperty() {\n        return dateFormatter;\n    }\n\n    /**\n     * Returns the value of {@link #dateFormatterProperty()}\n     *\n     * @return the date formatter\n     */\n    public final DateTimeFormatter getDateFormatter() {\n        return dateFormatter.get();\n    }\n\n    /**\n     * Sets the value of {@link #dateFormatterProperty()}\n     *\n     * @param formatter the date formatter, not {@code null}\n     */\n    public final void setDateFormatter(DateTimeFormatter formatter) {\n        this.dateFormatter.set(formatter);\n    }\n\n    // TIME FORMATTER\n\n    private final ObjectProperty<DateTimeFormatter> timeFormatter = new SimpleObjectProperty<>(this, \"timeFormatter\", DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT));\n\n    /**\n     * Gets the DateTimeFormatter instance, which is used to provide the format\n     * on the TimeScale for those labels that are displaying the time of day.\n     * By default it has a value of {@link FormatStyle#SHORT}.\n     *\n     * @return the time of day formatter.\n     */\n    protected ObjectProperty<DateTimeFormatter> timeFormatterProperty() {\n        return timeFormatter;\n    }\n\n    /**\n     * Returns the value of {@link #timeFormatterProperty()}\n     *\n     * @return the time of day formatter\n     */\n    public final DateTimeFormatter getTimeFormatter() {\n        return timeFormatterProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #timeFormatterProperty()}\n     *\n     * @param formatter the time of day formatter, not {@code null}\n     */\n    public void setTimeFormatter(DateTimeFormatter formatter) {\n        requireNonNull(formatter);\n        timeFormatterProperty().set(formatter);\n    }\n\n    // STYLE PROVIDERS\n\n    private static final Function<LocalDateTime, String> DEFAULT_STYLE_PROVIDER = (time) -> null;\n\n    private final ObjectProperty<Function<LocalDateTime, String>> timeStyleProvider = new SimpleObjectProperty<>(this, \"timeStyleProvider\", DEFAULT_STYLE_PROVIDER);\n\n    /**\n     * Gets the function, which is used to provide additional style properties for the labels\n     * that will display the times of a day (01:00, 02:00, ...).\n     *\n     * @return the style provider\n     */\n    public ObjectProperty<Function<LocalDateTime, String>> timeStyleProviderProperty() {\n        return timeStyleProvider;\n    }\n\n    /**\n     * Returns the value of {@link #timeStyleProviderProperty()}.\n     *\n     * @return the style provider\n     */\n    public Function<LocalDateTime, String> getTimeStyleProvider() {\n        return timeStyleProvider.get();\n    }\n\n    /**\n     * Sets the value of {@link #timeStyleProviderProperty()}.\n     *\n     * @param styleProvider time labels style provider, not {@code null}\n     */\n    public void setTimeStyleProvider(Function<LocalDateTime, String> styleProvider) {\n        this.timeStyleProvider.set(styleProvider);\n    }\n\n    private final ObjectProperty<Function<LocalDateTime, String>> dateStyleProvider = new SimpleObjectProperty<>(this, \"dateStyleProvider\", DEFAULT_STYLE_PROVIDER);\n\n    /**\n     * Gets the function, which is used to provide additional styling for the labels\n     * used to display dates.\n     *\n     * @return the style function for date labels\n     */\n    public ObjectProperty<Function<LocalDateTime, String>> dateStyleProviderProperty() {\n        return dateStyleProvider;\n    }\n\n    /**\n     * Returns the value of {@link #dateStyleProviderProperty()}.\n     *\n     * @return the style function for date labels\n     */\n    public Function<LocalDateTime, String> getDateStyleProvider() {\n        return dateStyleProvider.get();\n    }\n\n    /**\n     * Sets the value of {@link #dateStyleProviderProperty()}.\n     *\n     * @param styleProvider date labels style provider, not {@code null}\n     */\n    public void setDateStyleProvider(Function<LocalDateTime, String> styleProvider) {\n        this.dateStyleProvider.set(styleProvider);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/VirtualGrid.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.util.Util;\n\nimport java.time.DayOfWeek;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A virtual grid used to make entries snap to virtual grid lines while being\n * edited.\n *\n * @see DateControl#setVirtualGrid(VirtualGrid)\n */\npublic class VirtualGrid {\n\n    /**\n     * The virtual grid in {@link DateControl} can never be null, hence we need\n     * a virtual grid that does nothing, the OFF grid.\n     *\n     * @see DateControl#setVirtualGrid(VirtualGrid)\n     */\n    public static final VirtualGrid OFF = new VirtualGrid(Messages.getString(\"VirtualGrid.OFF\"), Messages.getString(\"VirtualGrid.OFF_SHORT\"),\n            ChronoUnit.SECONDS, 1) {\n\n        @Override\n        public Instant adjustTime(Instant instant, ZoneId zoneId, boolean roundUp, DayOfWeek firstDayOfWeek) {\n            return instant;\n        }\n\n        @Override\n        public LocalDateTime adjustTime(LocalDateTime time, boolean roundUp, DayOfWeek firstDayOfWeek) {\n            return time;\n        }\n\n        @Override\n        public ZonedDateTime adjustTime(ZonedDateTime time, boolean roundUp, DayOfWeek firstDayOfWeek) {\n            return time;\n        }\n    };\n\n    private final String name;\n    private final String shortName;\n    private final ChronoUnit unit;\n    private final int amount;\n\n    /**\n     * Constructs a new virtual grid.\n     *\n     * @param name\n     *            the name of the grid (e.g. \"15 Minutes\")\n     * @param shortName\n     *            the short name of the grid (e.g. \"15 Min.\")\n     * @param unit\n     *            the time unit of the grid (seconds, minutes, hours)\n     * @param amount\n     *            the amount of the unit (5, 10, 15 minutes)\n     */\n    public VirtualGrid(String name, String shortName, ChronoUnit unit, int amount) {\n        this.name = requireNonNull(name);\n        this.shortName = requireNonNull(shortName);\n        this.unit = requireNonNull(unit);\n\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"grid amount must be larger than 0 but was \" + amount);\n        }\n\n        this.amount = amount;\n    }\n\n    /**\n     * Returns the grid name that can be used for grid selection controls.\n     *\n     * @return the name of the grid settings\n     */\n    public final String getName() {\n        return name;\n    }\n\n    /**\n     * Returns the grid short name that can be used for grid selection controls.\n     *\n     * @return the short name of the grid settings\n     */\n    public final String getShortName() {\n        return shortName;\n    }\n\n    /**\n     * The temporal unit used for the grid.\n     *\n     * @return the temporal unit of the grid\n     */\n    public final ChronoUnit getUnit() {\n        return unit;\n    }\n\n    /**\n     * The number of units used for the grid.\n     *\n     * @return the number of units\n     */\n    public final int getAmount() {\n        return amount;\n    }\n\n    /**\n     * Adjusts the given time either rounding it up or down.\n     *\n     * @param time\n     *            the time to adjust\n     * @param roundUp\n     *            the rounding direction\n     * @param firstDayOfWeek\n     *            the first day of the week (needed for rounding weeks)\n     * @return the adjusted time\n     */\n    public ZonedDateTime adjustTime(ZonedDateTime time, boolean roundUp, DayOfWeek firstDayOfWeek) {\n        Instant instant = time.toInstant();\n        ZoneId zoneId = time.getZone();\n        instant = adjustTime(instant, zoneId, roundUp, firstDayOfWeek);\n        return ZonedDateTime.ofInstant(instant, zoneId);\n    }\n\n    /**\n     * Adjusts the given instant either rounding it up or down.\n     *\n     * @param instant\n     *            the instant to adjust\n     * @param zoneId\n     *            the time zone\n     * @param roundUp\n     *            the rounding direction\n     * @param firstDayOfWeek\n     *            the first day of the week (needed for rounding weeks)\n     * @return the adjusted instant\n     */\n    public Instant adjustTime(Instant instant, ZoneId zoneId, boolean roundUp, DayOfWeek firstDayOfWeek) {\n        requireNonNull(instant);\n        requireNonNull(zoneId);\n        requireNonNull(firstDayOfWeek);\n\n        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);\n        if (roundUp) {\n            zonedDateTime = zonedDateTime.plus(getAmount(), getUnit());\n        }\n\n        zonedDateTime = Util.truncate(zonedDateTime, getUnit(), getAmount(), firstDayOfWeek);\n\n        return Instant.from(zonedDateTime);\n    }\n\n    /**\n     * Adjusts the given time either rounding it up or down.\n     *\n     * @param time\n     *            the time to adjust\n     * @param roundUp\n     *            the rounding direction\n     * @param firstDayOfWeek\n     *            the first day of the week (needed for rounding weeks)\n     * @return the adjusted time\n     */\n    public LocalDateTime adjustTime(LocalDateTime time, boolean roundUp, DayOfWeek firstDayOfWeek) {\n        requireNonNull(time);\n\n        if (roundUp) {\n            time = time.plus(getAmount(), getUnit());\n        }\n\n        return Util.truncate(time, getUnit(), getAmount(), firstDayOfWeek);\n    }\n\n\n    @Override\n    public String toString() {\n        return \"VirtualGrid [name=\" + name + \", shortName=\" + shortName + \", unit=\" + unit + \", amount=\" + amount + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/WeekDayHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.WeekDayHeaderViewSkin;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Skin;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A view for displaying the names of the week days in the {@link DetailedWeekView}\n * control. A cell factory can be used to customize the appearance of the names.\n *\n * <img src=\"doc-files/week-weekdays.png\" alt=\"Week Weekdays\">\n */\npublic class WeekDayHeaderView extends DateControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"weekday-header-view\";\n\n    /**\n     * Constructs a new week day header view with the given number of days.\n     *\n     * @param numberOfDays the number of days to show\n     */\n    public WeekDayHeaderView(int numberOfDays) {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        setNumberOfDays(numberOfDays);\n\n        dateProperty().addListener(it -> {\n            LocalDate date = getDate();\n            if (isAdjustToFirstDayOfWeek()) {\n                date = Util.adjustToFirstDayOfWeek(date, getFirstDayOfWeek());\n            }\n\n            startDate.set(date);\n            endDate.set(date.plusDays(getNumberOfDays() - 1));\n        });\n\n        startDate.set(getDate());\n        endDate.set(getDate().plusDays(getNumberOfDays() - 1));\n\n        setCellFactory(date -> new WeekDayHeaderCell(this));\n    }\n\n    /**\n     * Constructs a new week day header view.\n     */\n    public WeekDayHeaderView() {\n        this(7);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekDayHeaderViewSkin(this);\n    }\n\n    private final ObjectProperty<Callback<WeekDayHeaderView, WeekDayHeaderCell>> cellFactory = new SimpleObjectProperty<>(this, \"cellFactory\");\n\n    /**\n     * A cell factory used for creating instances of {@link WeekDayHeaderCell} that will\n     * be used to display the weekend day names.\n     *\n     * @return the cell factory\n     */\n    public final ObjectProperty<Callback<WeekDayHeaderView, WeekDayHeaderCell>> cellFactoryProperty() {\n        return cellFactory;\n    }\n\n    /**\n     * Returns the value of {@link #cellFactoryProperty()}.\n     *\n     * @return the cell factory\n     */\n    public final Callback<WeekDayHeaderView, WeekDayHeaderCell> getCellFactory() {\n        return cellFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #cellFactoryProperty()}.\n     *\n     * @param factory the cell factory\n     */\n    public final void setCellFactory(Callback<WeekDayHeaderView, WeekDayHeaderCell> factory) {\n        requireNonNull(factory);\n        cellFactoryProperty().set(factory);\n    }\n\n    private final ObjectProperty<Callback<WeekDayHeaderView, Region>> separatorFactory = new SimpleObjectProperty<>(this, \"separatorFactory\", it -> {\n        Region region = new Region();\n        region.getStyleClass().add(\"weekday-separator\");\n        return region;\n    });\n\n\n    public final Callback<WeekDayHeaderView, Region> getSeparatorFactory() {\n        return separatorFactory.get();\n    }\n\n    /**\n     * A factory used for creating (optional) vertical separators between the week day headers.\n     *\n     * @return the separator factory\n     */\n    public final ObjectProperty<Callback<WeekDayHeaderView, Region>> separatorFactoryProperty() {\n        return separatorFactory;\n    }\n\n    public final void setSeparatorFactory(Callback<WeekDayHeaderView, Region> separatorFactory) {\n        this.separatorFactory.set(separatorFactory);\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\", 7);\n\n    /**\n     * Stores the number of days that will be shown by this view. This value\n     * needs to be equivalent to the number of days shown by the\n     * {@link DetailedWeekView}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \" + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n\n    private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, \"adjustToFirstDayOfWeek\", true);\n\n    /**\n     * A flag used to indicate that the view should always show the first day of\n     * the week (e.g. \"Monday\") at its beginning even if the\n     * {@link #dateProperty()} is set to another day (e.g. \"Thursday\").\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final BooleanProperty adjustToFirstDayOfWeekProperty() {\n        return adjustToFirstDayOfWeek;\n    }\n\n    /**\n     * Returns the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final boolean isAdjustToFirstDayOfWeek() {\n        return adjustToFirstDayOfWeekProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @param adjust if true the view will always show the first day of the week\n     */\n    public final void setAdjustToFirstDayOfWeek(boolean adjust) {\n        adjustToFirstDayOfWeekProperty().set(adjust);\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\");\n\n    /**\n     * The first date shown by the view.\n     *\n     * @return the first date shown by the view\n     */\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        return startDate;\n    }\n\n    /**\n     * Returns the value of {@link #startDateProperty()}.\n     *\n     * @return the start date\n     */\n    public final LocalDate getStartDate() {\n        return startDate.get();\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\");\n\n    /**\n     * The last date shown by the view.\n     *\n     * @return the last date shown by the view\n     */\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        return endDate;\n    }\n\n    /**\n     * A cell used by the {@link WeekDayHeaderView} to display the names of the\n     * weekdays (Mo. 5th, Tue. 6th, ...).\n     *\n     * @see WeekDayHeaderView#cellFactoryProperty()\n     */\n    public static class WeekDayHeaderCell extends Label {\n\n        /**\n         * Constructs a new date cell.\n         *\n         * @param view the weekday header view\n         */\n        public WeekDayHeaderCell(WeekDayHeaderView view) {\n            Objects.requireNonNull(view);\n            getStyleClass().add(\"cell\");\n\n            setMaxWidth(Double.MAX_VALUE);\n            dateProperty().addListener(it -> setText(getFormatter().format(getDate())));\n\n            if (view.isEnableHyperlinks()) {\n                getStyleClass().add(\"date-hyperlink\");\n                setOnMouseClicked(evt -> {\n                    if (evt.getButton() == MouseButton.PRIMARY && evt.getClickCount() == 1) {\n                        fireEvent(new RequestEvent(this, this, getDate()));\n                    }\n                });\n            }\n        }\n\n        private final ObjectProperty<DateTimeFormatter> formatter = new SimpleObjectProperty<>(this, \"\", DateTimeFormatter.ofPattern(Messages.getString(\"WeekDayHeaderView.CELL_DATE_FORMAT\")));\n\n        public final DateTimeFormatter getFormatter() {\n            return formatter.get();\n        }\n\n        /**\n         * The formatter to be used for the date.\n         *\n         * @return the date formatter\n         */\n        public final ObjectProperty<DateTimeFormatter> formatterProperty() {\n            return formatter;\n        }\n\n        public final void setFormatter(DateTimeFormatter formatter) {\n            this.formatter.set(formatter);\n        }\n\n        private final ObjectProperty<LocalDate> date = new SimpleObjectProperty<>(this, \"date\", LocalDate.now());\n\n        /**\n         * The date shown by this cell.\n         *\n         * @return the date\n         */\n        public final ObjectProperty<LocalDate> dateProperty() {\n            return date;\n        }\n\n        /**\n         * Sets the value of {@link #dateProperty()}.\n         *\n         * @param date the new date to show\n         */\n        public final void setDate(LocalDate date) {\n            dateProperty().set(date);\n        }\n\n        /**\n         * Returns the value of {@link #dateProperty()}.\n         *\n         * @return the date shown by the cell\n         */\n        public final LocalDate getDate() {\n            return dateProperty().get();\n        }\n    }\n\n    private static final String WEEK_DAY_HEADER_VIEW_CATEGORY = \"Week Day Header View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(numberOfDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setNumberOfDays((Integer) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getNumberOfDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_DAY_HEADER_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/WeekDayView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.WeekDayViewSkin;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.collections.MapChangeListener;\nimport javafx.scene.control.Skin;\n\n/**\n * A specialization of the regular {@link DayView} in order to support some customized\n * styling / customized behaviour. Additionally, this view always has a reference to the\n * {@link WeekView} where it is being used.\n */\npublic class WeekDayView extends DayView {\n\n    private static final String WEEKDAY_VIEW = \"weekday-view\";\n\n    /**\n     * Constructs a new day view.\n     */\n    public WeekDayView() {\n        getStyleClass().add(WEEKDAY_VIEW);\n\n        MapChangeListener<? super Object, ? super Object> propertiesListener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(\"week.view\")) {\n                    WeekView view = (WeekView) change.getValueAdded();\n                    weekView.set(view);\n                }\n            }\n        };\n\n        getProperties().addListener(propertiesListener);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekDayViewSkin(this);\n    }\n\n    private final ReadOnlyObjectWrapper<WeekView> weekView = new ReadOnlyObjectWrapper<>(this, \"weekView\");\n\n    /**\n     * The week view where the view is being used.\n     *\n     * @return the week view\n     */\n    public final ReadOnlyObjectProperty<WeekView> weekViewProperty() {\n        return weekView;\n    }\n\n    /**\n     * Returns the value of {@link #weekViewProperty()}.\n     *\n     * @return the week view\n     */\n    public final WeekView getWeekView() {\n        return weekView.get();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/WeekFieldsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.WeekFieldsViewSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyIntegerProperty;\nimport javafx.beans.property.ReadOnlyIntegerWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet;\n\nimport java.time.DayOfWeek;\nimport java.time.temporal.WeekFields;\nimport java.util.Optional;\n\n/**\n * A control used for editing the values of a {@link WeekFields} instance. These fields\n * are used for determine which weekday is considered the first day of the week. They also\n * store the minimum number of days that are required by the first week of the year in order\n * to be actually called a full week.\n */\npublic class WeekFieldsView extends CalendarFXControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"week-fields\";\n\n    /**\n     * Constructs a new view.\n     */\n    public WeekFieldsView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        weekFieldsProperty.addListener(it -> updateReadOnyProperties());\n        updateReadOnyProperties();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekFieldsViewSkin(this);\n    }\n\n    private final ObjectProperty<WeekFields> weekFieldsProperty = new SimpleObjectProperty<WeekFields>(this, \"weekFields\", WeekFields.ISO) {\n        @Override\n        public void setValue(WeekFields v) {\n            if (v == null) {\n                throw new IllegalArgumentException(\"week fields value can not be null\");\n            }\n            super.setValue(v);\n        }\n    };\n\n    /**\n     * The property used to store the current value of this view.\n     *\n     * @return the view's value / a week fields instance\n     */\n    public final ObjectProperty<WeekFields> weekFieldsProperty() {\n        return weekFieldsProperty;\n    }\n\n    /**\n     * Returns the value of {@link #weekFieldsProperty()}.\n     *\n     * @return the view's value / a week fields instance\n     */\n    public final WeekFields getWeekFields() {\n        return weekFieldsProperty.get();\n    }\n\n    /**\n     * Sets the value of {@link #weekFieldsProperty()}.\n     *\n     * @param fields the view's value / a week fields instance\n     */\n    public final void setWeekFields(WeekFields fields) {\n        this.weekFieldsProperty.set(fields);\n    }\n\n    private void updateReadOnyProperties() {\n        WeekFields fields = getWeekFields();\n        firstDayOfWeek.set(fields.getFirstDayOfWeek());\n        minimalDaysInFirstWeek.set(fields.getMinimalDaysInFirstWeek());\n    }\n\n    // day of week support\n\n    private final ReadOnlyObjectWrapper<DayOfWeek> firstDayOfWeek = new ReadOnlyObjectWrapper<>(this, \"firstDayOfWeek\");\n\n    /**\n     * A read-only property storing the \"first day of week\" as specified by {@link #weekFieldsProperty()}.\n     *\n     * @return the first day of the week\n     */\n    public final ReadOnlyObjectProperty<DayOfWeek> firstDayOfWeekProperty() {\n        return firstDayOfWeek.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #firstDayOfWeekProperty()}.\n     *\n     * @return the first day of the week\n     */\n    public final DayOfWeek getFirstDayOfWeek() {\n        return firstDayOfWeek.get();\n    }\n\n    // minimal days in first week support\n\n    private final ReadOnlyIntegerWrapper minimalDaysInFirstWeek = new ReadOnlyIntegerWrapper(this, \"minimalDaysInFirstWeek\");\n\n    /**\n     * A read-only property storing the \"minimal days in first week\" as specified by {@link #weekFieldsProperty()}.\n     *\n     * @return the minimal days in first week value\n     */\n    public final ReadOnlyIntegerProperty minimalDaysInFirstWeekProperty() {\n        return minimalDaysInFirstWeek.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #minimalDaysInFirstWeekProperty()}.\n     *\n     * @return the minimal days in first week value\n     */\n    public int getMinimalDaysInFirstWeek() {\n        return minimalDaysInFirstWeek.get();\n    }\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Class<?> getType() {\n                return WeekFields.class;\n            }\n\n            @Override\n            public String getCategory() {\n                return \"Week Fields View\";\n            }\n\n            @Override\n            public String getName() {\n                return \"Week Fields\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Week Fields\";\n            }\n\n            @Override\n            public Object getValue() {\n                return WeekFieldsView.this.getWeekFields();\n            }\n\n            @Override\n            public void setValue(Object o) {\n                WeekFieldsView.this.setWeekFields((WeekFields) o);\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(weekFieldsProperty);\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/WeekTimeScaleView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.WeekTimeScaleViewSkin;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.MapChangeListener;\nimport javafx.scene.control.Skin;\n\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\n\n/**\n * A specialization of the regular {@link TimeScaleView} to support a reference\n * to the {@link WeekView} where this scale is being used.\n */\npublic class WeekTimeScaleView extends TimeScaleView {\n\n    private final ObjectProperty<DateTimeFormatter> formatter = new SimpleObjectProperty<>(this, \"formatter\", DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT));\n\n    /**\n     * Constructs a new scale view.\n     */\n    public WeekTimeScaleView() {\n        MapChangeListener<? super Object, ? super Object> propertiesListener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(\"week.view\")) {\n                    detailedWeekView.set((DetailedWeekView) change.getValueAdded());\n                }\n            }\n        };\n\n        getProperties().addListener(propertiesListener);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekTimeScaleViewSkin(this);\n    }\n\n    private final ReadOnlyObjectWrapper<DetailedWeekView> detailedWeekView = new ReadOnlyObjectWrapper<>(this, \"detailedWeekView\");\n\n    /**\n     * The week view where this scale is being used.\n     *\n     * @return the week view\n     */\n    public final ReadOnlyObjectProperty<DetailedWeekView> detailedWeekViewProperty() {\n        return detailedWeekView.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #detailedWeekViewProperty()}.\n     *\n     * @return the week view\n     */\n    public final DetailedWeekView getDetailedWeekView() {\n        return detailedWeekView.get();\n    }\n\n    @Override\n    protected ObjectProperty<DateTimeFormatter> timeFormatterProperty() {\n        return formatter;\n    }\n\n    @Override\n    public void setTimeFormatter(DateTimeFormatter formatter) {\n        timeFormatterProperty().set(formatter);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/WeekView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.util.ViewHelper;\nimport impl.com.calendarfx.view.WeekViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Bounds;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet;\n\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport static java.time.temporal.ChronoField.DAY_OF_WEEK;\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A view for showing several week days in a row, normally seven. However the\n * view can be configured to show any number of days. A factory is used to\n * create instances of {@link WeekDayView} on the fly as needed. Another factory\n * is required for creating the child control {@link AllDayView}. The image\n * below shows the appearance of this view when it is embedded inside the\n * {@link DetailedWeekView}.\n *\n *\n * <img src=\"doc-files/week-view.png\" alt=\"Week View\">\n *\n */\npublic class WeekView extends DayViewBase {\n\n    private static final String DEFAULT_STYLE_CLASS = \"week-view\";\n\n    /**\n     * Constructs a new week view with seven days.\n     */\n    public WeekView() {\n        this(7);\n    }\n\n    /**\n     * Constructs a new week view with the given number of days.\n     *\n     * @param numberOfDays the number of days (day views)\n     */\n    public WeekView(int numberOfDays) {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        setWeekDayViewFactory(param -> new WeekDayView());\n        setNumberOfDays(numberOfDays);\n        setShowToday(true);\n\n        dateProperty().addListener(it -> updateStartAndEndDates());\n\n        updateStartAndEndDates();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekViewSkin(this);\n    }\n\n    @Override\n    public ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId) {\n        final WeekDayView view = getWeekDayViewAt(x);\n        if (view != null) {\n            return ZonedDateTime.ofInstant(ViewHelper.getInstantAt(view, y), getZoneId());\n        }\n\n        return super.getZonedDateTimeAt(x, y, zoneId);\n    }\n\n    private WeekDayView getWeekDayViewAt(double x) {\n        for (WeekDayView view : getWeekDayViews()) {\n            final Bounds bounds = view.getBoundsInParent();\n            if (bounds.getMinX() <= x && bounds.getMaxX() >= x) {\n                return view;\n            }\n        }\n\n        return null;\n    }\n\n    private final ObservableList<WeekDayView> weekDayViews = FXCollections.observableArrayList();\n\n    /**\n     * A list of the {@link WeekDayView} instances that the view created via the factory.\n     *\n     * @see #setWeekDayViewFactory(Callback)\n     *\n     * @return the currently used list of week day views (please note that this list is very volatile\n     * and will be updated very often.\n     */\n    public final ObservableList<WeekDayView> getWeekDayViews() {\n        return weekDayViews;\n    }\n\n    private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, \"numberOfDays\", 7);\n\n    /**\n     * Stores the number of days that will be shown by this view. This value\n     * will be 1 if the view is used in combination with the {@link DayView} and\n     * 7 if used together with the {@link DetailedWeekView}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final IntegerProperty numberOfDaysProperty() {\n        return numberOfDays;\n    }\n\n    /**\n     * Returns the value of {@link #numberOfDaysProperty()}.\n     *\n     * @return the number of days shown by the view\n     */\n    public final int getNumberOfDays() {\n        return numberOfDaysProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #numberOfDaysProperty()}.\n     *\n     * @param number the new number of days shown by the view\n     */\n    public final void setNumberOfDays(int number) {\n        if (number < 1) {\n            throw new IllegalArgumentException(\"invalid number of days, must be larger than 0 but was \"\n                    + number);\n        }\n\n        numberOfDaysProperty().set(number);\n    }\n\n    private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, \"adjustToFirstDayOfWeek\", true);\n\n    /**\n     * A flag used to indicate that the view should always show the first day of\n     * the week (e.g. \"Monday\") at its beginning even if the\n     * {@link #dateProperty()} is set to another day (e.g. \"Thursday\").\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final BooleanProperty adjustToFirstDayOfWeekProperty() {\n        return adjustToFirstDayOfWeek;\n    }\n\n    /**\n     * Returns the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @return true if the view always shows the first day of the week\n     */\n    public final boolean isAdjustToFirstDayOfWeek() {\n        return adjustToFirstDayOfWeekProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}.\n     *\n     * @param adjust if true the view will always show the first day of the week\n     */\n    public final void setAdjustToFirstDayOfWeek(boolean adjust) {\n        adjustToFirstDayOfWeekProperty().set(adjust);\n    }\n\n    /**\n     * The parameter object for the week day view factory.\n     *\n     * @see #weekDayViewFactoryProperty()\n     */\n    public static final class WeekDayParameter {\n\n        private final WeekView weekView;\n\n        /**\n         * Constructs a new parameter object.\n         *\n         * @param weekView the week view for which the week day view will be used\n         */\n        public WeekDayParameter(WeekView weekView) {\n            this.weekView = Objects.requireNonNull(weekView);\n        }\n\n        /**\n         * Returns the week view where the day view will be used.\n         *\n         * @return the week view\n         */\n        public WeekView getWeekView() {\n            return weekView;\n        }\n    }\n\n    private final ObjectProperty<Callback<WeekDayParameter, WeekDayView>> weekDayViewFactory = new SimpleObjectProperty<>(this, \"weekDayViewFactory\");\n\n    /**\n     * A factory used for creating instances of {@link WeekDayView} on the fly\n     * as required.\n     *\n     * @return the week day view factory\n     */\n    public final ObjectProperty<Callback<WeekDayParameter, WeekDayView>> weekDayViewFactoryProperty() {\n        return weekDayViewFactory;\n    }\n\n    /**\n     * Returns the value of {@link #weekDayViewFactoryProperty()}.\n     *\n     * @return the week day view factory\n     */\n    public final Callback<WeekDayParameter, WeekDayView> getWeekDayViewFactory() {\n        return weekDayViewFactoryProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #weekDayViewFactoryProperty()}.\n     *\n     * @param factory the new factory\n     */\n    public final void setWeekDayViewFactory(Callback<WeekDayParameter, WeekDayView> factory) {\n        requireNonNull(factory);\n        weekDayViewFactoryProperty().set(factory);\n    }\n\n    private void updateStartAndEndDates() {\n        LocalDate date = calculateStartDate();\n        startDate.set(date);\n        endDate.set(date.plusDays(getNumberOfDays() - 1));\n    }\n\n    private LocalDate calculateStartDate() {\n        LocalDate startDate = getDate();\n\n        if (isAdjustToFirstDayOfWeek()) {\n            LocalDate newStartDate = startDate.with(DAY_OF_WEEK, getFirstDayOfWeek().getValue());\n            if (newStartDate.isAfter(startDate)) {\n                startDate = newStartDate.minusWeeks(1);\n            } else {\n                startDate = newStartDate;\n            }\n        }\n\n        return startDate;\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\");\n\n    /**\n     * The earliest date shown by the view.\n     *\n     * @return the earliest date shown\n     */\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        return startDate;\n    }\n\n    /**\n     * Returns the value of {@link #startDateProperty()}.\n     *\n     * @return the earliest date shown\n     */\n    public final LocalDate getStartDate() {\n        return startDate.get();\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\");\n\n    /**\n     * The latest date shown by the view.\n     *\n     * @return the latest date shown\n     */\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        return endDate;\n    }\n\n    /**\n     * Returns the value of {@link #endDateProperty()}.\n     *\n     * @return the latest date shown\n     */\n    public final LocalDate getEndDate() {\n        return endDate.get();\n    }\n\n    private final ObjectProperty<Callback<WeekView, Region>> separatorFactory = new SimpleObjectProperty<>(this, \"separatorFactory\", it-> {\n        Region region = new Region();\n        region.getStyleClass().add(\"weekday-separator\");\n        return region;\n    });\n\n\n    public final Callback<WeekView, Region> getSeparatorFactory() {\n        return separatorFactory.get();\n    }\n\n    /**\n     * A factory used for creating (optional) vertical separators between the week days.\n     *\n     * @return the separator factory\n     */\n    public final ObjectProperty<Callback<WeekView, Region>> separatorFactoryProperty() {\n        return separatorFactory;\n    }\n\n    public final void setSeparatorFactory(Callback<WeekView, Region> separatorFactory) {\n        this.separatorFactory.set(separatorFactory);\n    }\n\n    private static final String WEEK_VIEW_CATEGORY = \"Week View\";\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(numberOfDaysProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setNumberOfDays((Integer) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getNumberOfDays();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Integer.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Number of Days\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(adjustToFirstDayOfWeekProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setAdjustToFirstDayOfWeek((Boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isAdjustToFirstDayOfWeek();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Adjust to first day of week\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Adjust to first day of week\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(startDateProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n            }\n\n            @Override\n            public Object getValue() {\n                return getStartDate();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Start date (read-only)\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Start date (read-only)\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(endDateProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n            }\n\n            @Override\n            public Object getValue() {\n                return getEndDate();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return LocalDate.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"End date (read-only)\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"End date (read-only)\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/YearMonthView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.YearMonthViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.util.Optional;\n\nimport static com.calendarfx.view.YearMonthView.ClickBehaviour.PERFORM_SELECTION;\nimport static java.lang.Double.MAX_VALUE;\nimport static java.util.Objects.requireNonNull;\nimport static javafx.geometry.Pos.CENTER;\n\n/**\n * Displays a given month of a given year. The view can be configured in many\n * ways:\n * <ul>\n * <li>Show / hide the name of the month</li>\n * <li>Show / hide the year</li>\n * <li>Show / hide arrow buttons for changing the month</li>\n * <li>Show / hide arrow buttons for changing the year</li>\n * <li>Show / hide today</li>\n * <li>Show / hide a button for going to today</li>\n * <li>Show / hide usage colors</li>\n * </ul>\n * Additionally the application can choose from two different behaviours when\n * the user clicks on a date:\n * <ol>\n * <li>Perform a selection / select the date</li>\n * <li>Show details of the date (by default shows a popover with all entries on\n * that date)</li>\n * </ol>\n * The image below shows the visual apperance of this control:\n *\n * <img src=\"doc-files/date-picker.png\" alt=\"Date Picker\">\n *\n */\npublic class YearMonthView extends MonthViewBase {\n\n    /**\n     * Constructs a new view.\n     */\n    public YearMonthView() {\n        getStyleClass().add(\"year-month-view\");\n        setCellFactory(view -> new DateCell());\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new YearMonthViewSkin(this);\n    }\n\n    /**\n     * The base date cell implementation for month views.\n     *\n     * @see #setCellFactory(Callback)\n     */\n    public static class DateCell extends Label {\n\n        private LocalDate date;\n\n        public DateCell() {\n            setFocusTraversable(false);\n            setMaxSize(MAX_VALUE, MAX_VALUE);\n            setAlignment(CENTER);\n        }\n\n        public final void setDate(LocalDate date) {\n            this.date = date;\n            update(date);\n        }\n\n        public final LocalDate getDate() {\n            return date;\n        }\n\n        protected void update(LocalDate date) {\n            setText(Integer.toString(date.getDayOfMonth()));\n        }\n    }\n\n    private final ObjectProperty<Callback<YearMonthView, DateCell>> cellFactory = new SimpleObjectProperty<>(this, \"cellFactory\");\n\n    /**\n     * A factory for creating alternative content for the month view. The image\n     * below shows the {@link YearMonthView} once with the default factory and\n     * once with an alternative factory that creates checkboxes.\n     *\n     * <img src=\"doc-files/month-cell-factory.png\" alt=\"Month Cell Factory\">\n     *\n     * @return the cell factory\n     */\n    public final ObjectProperty<Callback<YearMonthView, DateCell>> cellFactoryProperty() {\n        return cellFactory;\n    }\n\n    /**\n     * Sets the value of {@link #cellFactoryProperty()}.\n     *\n     * @param factory\n     *            the cell factory\n     */\n    public final void setCellFactory(Callback<YearMonthView, DateCell> factory) {\n        requireNonNull(factory);\n        cellFactoryProperty().set(factory);\n    }\n\n    /**\n     * Returns the value of {@link #cellFactoryProperty()}.\n     *\n     * @return the cell factory\n     */\n    public final Callback<YearMonthView, DateCell> getCellFactory() {\n        return cellFactoryProperty().get();\n    }\n\n    private final BooleanProperty showMonth = new SimpleBooleanProperty(this, \"showMonth\", true);\n\n    /**\n     * Show or hide the name of the month.\n     *\n     * @return true if the month name will be shown\n     */\n    public final BooleanProperty showMonthProperty() {\n        return showMonth;\n    }\n\n    /**\n     * Sets the value of {@link #showMonthProperty()}.\n     *\n     * @param show\n     *            if true the month will be shown\n     */\n    public final void setShowMonth(boolean show) {\n        showMonthProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showMonthProperty()}.\n     *\n     * @return true if the month name will be shown\n     */\n    public final boolean isShowMonth() {\n        return showMonthProperty().get();\n    }\n\n    private final BooleanProperty showYear = new SimpleBooleanProperty(this, \"showYear\", true);\n\n    /**\n     * Show or hide the year.\n     *\n     * @return true if the year will be shown\n     */\n    public final BooleanProperty showYearProperty() {\n        return showYear;\n    }\n\n    /**\n     * Sets the value of {@link #showYearProperty()}.\n     *\n     * @param show\n     *            if true the year will be shown\n     */\n    public final void setShowYear(boolean show) {\n        showYearProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showYearProperty()}.\n     *\n     * @return true if the year will be shown\n     */\n    public final boolean isShowYear() {\n        return showYearProperty().get();\n    }\n\n    private final BooleanProperty showTodayButton = new SimpleBooleanProperty(this, \"showTodayButton\", true);\n\n    /**\n     * Show or hide a button to quickly go to today's date.\n     *\n     * @return true if the button will be shown\n     */\n    public final BooleanProperty showTodayButtonProperty() {\n        return showTodayButton;\n    }\n\n    /**\n     * Sets the value of the {@link #showTodayButtonProperty()}.\n     *\n     * @param show\n     *            if true will show the button\n     */\n    public final void setShowTodayButton(boolean show) {\n        showTodayButtonProperty().set(show);\n    }\n\n    /**\n     * Returns the value of the {@link #showTodayButtonProperty()}.\n     *\n     * @return true if the button is shown\n     */\n    public final boolean isShowTodayButton() {\n        return showTodayButtonProperty().get();\n    }\n\n    private final BooleanProperty showMonthArrows = new SimpleBooleanProperty(this, \"showMonthArrows\", true);\n\n    /**\n     * Shows or hides the arrows to change the month.\n     *\n     * @return true if the arrows will be shown\n     */\n    public final BooleanProperty showMonthArrowsProperty() {\n        return showMonthArrows;\n    }\n\n    /**\n     * Sets the value of the {@link #showMonthArrowsProperty()}.\n     *\n     * @param show\n     *            if true will show the arrows\n     */\n    public final void setShowMonthArrows(boolean show) {\n        showMonthArrowsProperty().set(show);\n    }\n\n    /**\n     * Returns the value of the {@link #showMonthArrowsProperty()}.\n     *\n     * @return true if the arrows will be shown\n     */\n    public final boolean isShowMonthArrows() {\n        return showMonthArrowsProperty().get();\n    }\n\n    private final BooleanProperty showYearArrows = new SimpleBooleanProperty(this, \"showYearArrows\", true);\n\n    /**\n     * Shows or hides the arrows to change the year.\n     *\n     * @return true if the arrows will be shown\n     */\n    public final BooleanProperty showYearArrowsProperty() {\n        return showYearArrows;\n    }\n\n    /**\n     * Sets the value of the {@link #showYearArrowsProperty()}.\n     *\n     * @param show\n     *            if true will show the arrows\n     */\n    public final void setShowYearArrows(boolean show) {\n        showYearArrowsProperty().set(show);\n    }\n\n    /**\n     * Returns the value of the {@link #showYearArrowsProperty()}.\n     *\n     * @return true if the arrows will be shown\n     */\n    public final boolean isShowYearArrows() {\n        return showYearArrowsProperty().get();\n    }\n\n    private final BooleanProperty showUsageColors = new SimpleBooleanProperty(this, \"showUsageColors\", false);\n\n    /**\n     * Show or hide usage colors that are based on the number of entries on a\n     * given date. The image below shows those colors in action:\n     *\n     * <img src=\"doc-files/usage-colors.png\" alt=\"Usage Colors\">\n     *\n     * @return true if the usage colors will be shown\n     */\n    public final BooleanProperty showUsageColorsProperty() {\n        return showUsageColors;\n    }\n\n    /**\n     * Sets the value of {@link #showUsageColorsProperty()}.\n     *\n     * @param show\n     *            if true will show the colors\n     */\n    public final void setShowUsageColors(boolean show) {\n        showUsageColorsProperty().set(show);\n    }\n\n    /**\n     * Returns the value of the {@link #showUsageColorsProperty()}.\n     *\n     * @return true if the colors will be shown\n     */\n    public final boolean isShowUsageColors() {\n        return showUsageColorsProperty().get();\n    }\n\n    /**\n     * An enumerator to control the behaviour of the control when the user\n     * clicks on a date.\n     *\n     * @see YearMonthView#clickBehaviourProperty()\n     */\n    public enum ClickBehaviour {\n\n        /**\n         * A value used to make the control select the date on which the user clicked.\n         */\n        PERFORM_SELECTION,\n\n        /**\n         * A value used to make the control show some kind of dialog or popover to show\n         * details about the clicked date.\n         */\n        SHOW_DETAILS,\n\n        /**\n         * Do nothing when the user clicks on a date.\n         */\n        NONE\n    }\n\n    private final ObjectProperty<ClickBehaviour> clickBehaviour = new SimpleObjectProperty<>(this, \"clickBehaviour\", PERFORM_SELECTION);\n\n    /**\n     * The behaviour used when the user clicks on a date.\n     *\n     * @return the click behaviour\n     */\n    public final ObjectProperty<ClickBehaviour> clickBehaviourProperty() {\n        return clickBehaviour;\n    }\n\n    /**\n     * Sets the value of {@link #clickBehaviourProperty()}.\n     *\n     * @param behaviour the click behaviour\n     */\n    public final void setClickBehaviour(ClickBehaviour behaviour) {\n        requireNonNull(behaviour);\n        clickBehaviourProperty().set(behaviour);\n    }\n\n    /**\n     * Returns the value of {@link #clickBehaviourProperty()}.\n     *\n     * @return the click behaviour\n     */\n    public final ClickBehaviour getClickBehaviour() {\n        return clickBehaviourProperty().get();\n    }\n\n    private static final String MONTH_VIEW_CATEGORY = \"Month View\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(clickBehaviourProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setClickBehaviour((ClickBehaviour) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getClickBehaviour();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return ClickBehaviour.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Click Behaviour\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Click behaviour\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showMonthProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowMonth((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowMonth();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Month\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the name of the month\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTodayButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowTodayButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowTodayButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Today Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the 'today' button\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showUsageColorsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowUsageColors((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowUsageColors();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Usage Colors\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Usage Colors\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showYearProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowYear((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowYear();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Year\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the year\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showYearArrowsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowYearArrows((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowYearArrows();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Year Arrows\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the year adjuster arrows\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showMonthArrowsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowMonthArrows((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowMonthArrows();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Month Arrows\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show or hide the month adjustment arrows\";\n            }\n\n            @Override\n            public String getCategory() {\n                return MONTH_VIEW_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/YearView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport impl.com.calendarfx.view.YearViewSkin;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.scene.control.Skin;\nimport javafx.util.Callback;\n\nimport java.time.Month;\nimport java.time.Year;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * A view that shows exactly twelve months of a year via the help of\n * the {@link YearMonthView} control. The image below shows the year view\n * with usage coloring enabled.\n *\n * @see YearMonthView#setShowUsageColors(boolean)\n * @see DateControl#setUsagePolicy(Callback)\n *\n * <h3>Screenshot</h3>\n * <img src=\"doc-files/year-view.png\" alt=\"Year View\">\n */\npublic class YearView extends DateControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"year-view\";\n\n    private final Map<Month, YearMonthView> viewMap = new HashMap<>();\n\n    /**\n     * Constructs a new year view.\n     */\n    public YearView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n\n        dateProperty().addListener(evt -> year.set(Year.from(getDate())));\n\n        for (Month month : Month.values()) {\n            YearMonthView view = new YearMonthView();\n            viewMap.put(month, view);\n            view.setShowYear(false);\n        }\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new YearViewSkin(this);\n    }\n\n    private final ReadOnlyObjectWrapper<Year> year = new ReadOnlyObjectWrapper<>(this, \"year\", Year.from(getToday()));\n\n    /**\n     * Reports the year shown by the control.\n     *\n     * @return the year and month\n     */\n    public final ReadOnlyObjectProperty<Year> yearProperty() {\n        return year.getReadOnlyProperty();\n    }\n\n    /**\n     * Returns the value of {@link #yearProperty()}.\n     *\n     * @return the year\n     */\n    public final Year getYear() {\n        return yearProperty().get();\n    }\n\n    /**\n     * Returns the view that is used for displaying the given month of the year.\n     *\n     * @param month\n     *            the month\n     * @return a view for the given month\n     */\n    public final YearMonthView getMonthView(Month month) {\n        return viewMap.get(month);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/ZonedDateTimeProvider.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\n\n/**\n * An interface that needs to be implemented by those date controls that want to\n * support the creation of new entries with a double click. For this to work\n * they need to supply the time at the given click location.\n */\npublic interface ZonedDateTimeProvider {\n\n    /**\n     * Returns the time at the given location.\n     *\n     * @param x\n     *            the x coordinate of the input event\n     * @param y\n     *            the y coordinate of the input event\n     * @param zoneId\n     *            the zoneID for which we query the time\n     * @return the time at the given location\n     */\n    ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId);\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * Custom controls for visualizing calendars for a day, a week, a month, or a year.\n * Some additional custom controls for working with these views.\n */\npackage com.calendarfx.view;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/DayPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.page;\n\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DetailedDayView;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.MonthView;\nimport com.calendarfx.view.TimeScaleView;\nimport com.calendarfx.view.YearMonthView;\nimport com.calendarfx.view.print.ViewType;\nimport impl.com.calendarfx.view.page.DayPageSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.HBox;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.control.PropertySheet.Item;\nimport org.controlsfx.control.SegmentedButton;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.time.format.DateTimeFormatter;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A composite view focused on displaying calendar information for a single day.\n * The view consists of the page \"chrome\" inherited from the superclass, a\n * {@link DayView}, an {@link AllDayView}, a {@link TimeScaleView}, a\n * {@link YearMonthView} for picking a new date, and an {@link AgendaView}.\n *\n * <img width=\"100%\" src=\"doc-files/day-page.png\" alt=\"Day Page\">\n */\npublic class DayPage extends PageBase {\n\n    private final AgendaView agendaView;\n    private final YearMonthView yearMonthView;\n    private final DetailedDayView detailedDayView;\n    private final Node toolBarControls;\n    private HBox toolbarControls;\n\n    /**\n     * Constructs a new day page.\n     */\n    public DayPage() {\n        super();\n\n        getStyleClass().add(\"day-page\");\n\n        setDateTimeFormatter(\n                DateTimeFormatter.ofPattern(Messages.getString(\"DayPage.DATE_FORMATTER\")));\n\n        this.agendaView = new AgendaView();\n        this.yearMonthView = new YearMonthView();\n        this.detailedDayView = new DetailedDayView();\n        this.toolBarControls = createToolBarControls();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new DayPageSkin(this);\n    }\n\n    @Override\n    public Node getToolBarControls() {\n        return toolBarControls;\n    }\n\n    /**\n     * Creates the node used for the page-specific part of the calendar\n     * view.\n     *\n     * @return the toolbar controls specific for this page\n     */\n    protected Node createToolBarControls() {\n        FontIcon listIcon = new FontIcon(FontAwesome.LIST);\n        listIcon.getStyleClass().addAll(\"button-icon\");\n\n        ToggleButton agendaOnlyButton = new ToggleButton();\n        agendaOnlyButton.setMaxHeight(Double.MAX_VALUE);\n        agendaOnlyButton.setGraphic(listIcon);\n        agendaOnlyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n        FontIcon calendarIcon = new FontIcon(FontAwesome.CALENDAR);\n        calendarIcon.getStyleClass().addAll(\"button-icon\");\n\n        ToggleButton dayOnlyButton = new ToggleButton();\n        dayOnlyButton.setMaxHeight(Double.MAX_VALUE);\n        dayOnlyButton.setGraphic(calendarIcon);\n        dayOnlyButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n        FontIcon standardIcon = new FontIcon(FontAwesome.COLUMNS);\n        standardIcon.getStyleClass().addAll(\"button-icon\");\n\n        ToggleButton standardButton = new ToggleButton();\n        standardButton.setMaxHeight(Double.MAX_VALUE);\n        standardButton.setGraphic(standardIcon);\n        standardButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n        agendaOnlyButton.setOnAction(evt -> setDayPageLayout(DayPageLayout.AGENDA_ONLY));\n        dayOnlyButton.setOnAction(evt -> setDayPageLayout(DayPageLayout.DAY_ONLY));\n        standardButton.setOnAction(evt -> setDayPageLayout(DayPageLayout.STANDARD));\n\n        SegmentedButton segmentedButton = new SegmentedButton(agendaOnlyButton, standardButton, dayOnlyButton);\n        segmentedButton.getStyleClass().add(\"layout-button\");\n        segmentedButton.visibleProperty().bind(showDayPageLayoutControlsProperty());\n        segmentedButton.setMaxHeight(Double.MAX_VALUE);\n\n        updateDayPageLayoutButtons(agendaOnlyButton, dayOnlyButton, standardButton);\n        dayPageLayout.addListener((observable, oldValue, newValue) -> updateDayPageLayoutButtons(agendaOnlyButton, dayOnlyButton, standardButton));\n\n        agendaOnlyButton.setTooltip(new Tooltip(Messages.getString(\"DayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST\")));\n        dayOnlyButton.setTooltip(new Tooltip(Messages.getString(\"DayPage.TOOLTIP_MAXIMIZE_DAY_VIEW\")));\n        standardButton.setTooltip(new Tooltip(Messages.getString(\"DayPage.TOOLTIP_STANDARD_LAYOUT\")));\n\n        FontIcon layoutIcon = new FontIcon(FontAwesome.TABLE);\n        layoutIcon.getStyleClass().addAll(\"button-icon\", \"layout-button-icon\");\n\n        ToggleButton layoutButton = new ToggleButton();\n        layoutButton.setMaxHeight(Double.MAX_VALUE);\n        layoutButton.setTooltip(new Tooltip(Messages.getString(\"DayPage.TOOLTIP_LAYOUT\")));\n        layoutButton.setId(\"layout-button\");\n        layoutButton.setGraphic(layoutIcon);\n        layoutButton.setSelected(getLayout().equals(Layout.SWIMLANE));\n        layoutButton.setOnAction(evt -> {\n            if (layoutButton.isSelected()) {\n                setLayout(Layout.SWIMLANE);\n            } else {\n                setLayout(Layout.STANDARD);\n            }\n        });\n\n        toolbarControls = new HBox();\n        toolbarControls.setSpacing(10);\n\n        updateToolBarControls(segmentedButton, layoutButton);\n\n        showLayoutButtonProperty().addListener(it -> updateToolBarControls(segmentedButton, layoutButton));\n\n        return toolbarControls;\n    }\n\n    private void updateDayPageLayoutButtons(ToggleButton agendaOnlyButton,ToggleButton dayOnlyButton,ToggleButton standardButton) {\n        switch (dayPageLayout.get()) {\n            case AGENDA_ONLY:\n                agendaOnlyButton.setSelected(true);\n                break;\n            case DAY_ONLY:\n                dayOnlyButton.setSelected(true);\n                break;\n            case STANDARD:\n                standardButton.setSelected(true);\n                break;\n            default:\n                break;\n        }\n    }\n\n    private void updateToolBarControls(SegmentedButton segmentedButton, ToggleButton layoutButton) {\n        if (isShowLayoutButton()) {\n            toolbarControls.getChildren().setAll(layoutButton, segmentedButton);\n        } else {\n            toolbarControls.getChildren().setAll(segmentedButton);\n        }\n    }\n\n    private final BooleanProperty showLayoutButton = new SimpleBooleanProperty(this, \"showLayoutButton\", true);\n\n    /**\n     * Controls whether the \"layout\" button (to toggle between standard and\n     * swimlane layout) in the upper left corner of the control will be shown to\n     * the user or not.\n     *\n     * @return true if the \"print\" button will be accessible by the user\n     */\n    public final BooleanProperty showLayoutButtonProperty() {\n        return showLayoutButton;\n    }\n\n    /**\n     * Returns the value of {@link #showLayoutButtonProperty()}.\n     *\n     * @return true if the \"layout\" button will be accessible by the user\n     */\n    public final boolean isShowLayoutButton() {\n        return showLayoutButton.get();\n    }\n\n    /**\n     * Sets the value of {@link #showLayoutButtonProperty()}.\n     *\n     * @param show if true the \"layout\" button will be accessible by the user\n     */\n    public final void setShowLayoutButton(boolean show) {\n        showLayoutButton.set(show);\n    }\n\n    /**\n     * An enumerator used for telling the {@link DayPage} view to show more or\n     * less content.\n     */\n    public enum DayPageLayout {\n\n        /**\n         * Specifies that the view will display a {@link DayView}, a\n         * {@link MonthView}, and an {@link AgendaView}.\n         */\n        STANDARD,\n\n        /**\n         * Only displays an {@link AgendaView}.\n         */\n        AGENDA_ONLY,\n\n        /**\n         * Only displays the {@link DayView}.\n         */\n        DAY_ONLY\n    }\n\n    private final ObjectProperty<DayPageLayout> dayPageLayout = new SimpleObjectProperty<>(\n            this, \"dayPageLayout\", DayPageLayout.STANDARD);\n\n    /**\n     * Stores the currently requested layout for the {@link DayPage}. The layout\n     * determines if the page will show only the {@link DayView} or a more\n     * complext UI with the additional {@link MonthView} for picking a date, and\n     * an {@link AgendaView} with the calendar entries for the next couple of\n     * weeks.\n     *\n     * @return the day page layout\n     */\n    public final ObjectProperty<DayPageLayout> dayPageLayoutProperty() {\n        return dayPageLayout;\n    }\n\n    /**\n     * Sets the value of {@link #dayPageLayoutProperty()}.\n     *\n     * @param layout\n     *            the layout\n     */\n    public final void setDayPageLayout(DayPageLayout layout) {\n        requireNonNull(layout);\n        dayPageLayoutProperty().set(layout);\n    }\n\n    /**\n     * Returns the value of {@link #dayPageLayoutProperty()}.\n     *\n     * @return the layout\n     */\n    public final DayPageLayout getDayPageLayout() {\n        return dayPageLayoutProperty().get();\n    }\n\n    private final BooleanProperty showDayPageLayoutControls = new SimpleBooleanProperty(\n            this, \"showDayPageLayoutControls\", true);\n\n    /**\n     * Determines if the controls for switching between different layouts of\n     * this view will be shown to the user or not.\n     *\n     * @return true if the controls will be shown\n     */\n    public final BooleanProperty showDayPageLayoutControlsProperty() {\n        return showDayPageLayoutControls;\n    }\n\n    /**\n     * Returns the value of {@link #showDayPageLayoutControlsProperty()}.\n     *\n     * @return true if the controls will be shown\n     */\n    public final boolean isShowDayPageLayoutControls() {\n        return showDayPageLayoutControlsProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #showDayPageLayoutControlsProperty()}.\n     *\n     * @param show\n     *            if true the controls will be shown\n     */\n    public final void setShowDayPageLayoutControls(boolean show) {\n        showDayPageLayoutControlsProperty().set(show);\n    }\n\n    /**\n     * Returns the agenda view child control.\n     *\n     * @return the agenda view\n     */\n    public final AgendaView getAgendaView() {\n        return agendaView;\n    }\n\n    /**\n     * Returns the date picker child control.\n     *\n     * @return the date picker view\n     */\n    public final YearMonthView getYearMonthView() {\n        return yearMonthView;\n    }\n\n    /**\n     * Returns the day view.\n     *\n     * @return the day view\n     */\n    public final DetailedDayView getDetailedDayView() {\n        return detailedDayView;\n    }\n\n    @Override\n    public final void goForward() {\n        setDate(getDate().plusDays(1));\n    }\n\n    @Override\n    public final void goBack() {\n        setDate(getDate().minusDays(1));\n    }\n\n    @Override\n    public final ViewType getPrintViewType() {\n        return ViewType.DAY_VIEW;\n    }\n\n    private static final String DAY_PAGE_CATEGORY = \"Day Page\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(dayPageLayoutProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setDayPageLayout((DayPageLayout) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getDayPageLayout();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return DayPageLayout.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Day Page Layout\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Layout of the day page\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showDayPageLayoutControlsProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowDayPageLayoutControls((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowDayPageLayoutControls();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Day Page Layout Controls\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show Day Page Layout Controls\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_PAGE_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showLayoutButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowLayoutButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowLayoutButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Layout Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the button to toggle the layout or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return DAY_PAGE_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/MonthPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.page;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.MonthView;\nimport com.calendarfx.view.print.ViewType;\nimport impl.com.calendarfx.view.page.MonthPageSkin;\nimport javafx.scene.control.Skin;\n\nimport java.time.format.DateTimeFormatter;\n\n/**\n * A composite view focused on displaying calendar information for a single\n * month. The view consists of the page \"chrome\" inherited from the superclass\n * and a {@link MonthView}.\n *\n * <img width=\"100%\" src=\"doc-files/month-page.png\" alt=\"Month Page\">\n */\npublic class MonthPage extends PageBase {\n\n    private final MonthView monthView;\n\n    /**\n     * Constructs a new month page.\n     */\n    public MonthPage() {\n        super();\n\n        getStyleClass().add(\"month-page\");\n\n        this.monthView = new MonthView();\n\n        setDateTimeFormatter(DateTimeFormatter.ofPattern(Messages.getString(\"MonthPage.DATE_FORMAT\")));\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new MonthPageSkin(this);\n    }\n\n    /**\n     * Returns the week view child control. Most of the visualization in this\n     * page is done by this view. The page only adds its chrome.\n     *\n     * @return the week view\n     */\n    public final MonthView getMonthView() {\n        return monthView;\n    }\n\n    @Override\n    public final void goForward() {\n        setDate(getDate().plusMonths(1).withDayOfMonth(1));\n    }\n\n    @Override\n    public final void goBack() {\n        setDate(getDate().minusMonths(1).withDayOfMonth(1));\n    }\n\n    @Override\n    public final ViewType getPrintViewType() {\n        return ViewType.MONTH_VIEW;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/PageBase.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.page;\n\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.print.ViewType;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.format.DateTimeFormatter;\nimport java.util.Optional;\n\nimport static java.time.format.FormatStyle.MEDIUM;\nimport static java.util.Objects.requireNonNull;\n\n/**\n * The common superclass for all page views. It adds a chrome to the pages so\n * that the user can see the current date in the upper right corner. Navigation\n * controls can be found in the upper left corner.\n */\npublic abstract class PageBase extends DateControl {\n\n    /**\n     * Constructs a new page.\n     */\n    protected PageBase() {\n\n        /*\n         * Pages will take whatever space they can get. If they become too small for\n         * their content then they should react responsive and hide some of the content.\n         */\n        setMinSize(0, 0);\n        getStyleClass().add(\"calendar-page\");\n    }\n\n    /**\n     * Returns one or more controls that can be added to the toolbar by the\n     * surrounding container, e.g. the {@link CalendarView}.\n     *\n     * @return extra toolbar controls\n     */\n    public Node getToolBarControls() {\n        return null;\n    }\n\n    private final ObjectProperty<DateTimeFormatter> dateTimeFormatter = new SimpleObjectProperty<>(this, \"datePattern\", DateTimeFormatter.ofLocalizedDate(MEDIUM));\n\n    /**\n     * A formatter for the date shown in the upper right corner. Each page has\n     * its own formatting requirements. The {@link DayPage} displays a full\n     * date, while the {@link YearPage} only shows the current year.\n     *\n     * @return the date formatter\n     */\n    public final ObjectProperty<DateTimeFormatter> dateTimeFormatterProperty() {\n        return dateTimeFormatter;\n    }\n\n    /**\n     * Returns the value of {@link #dateTimeFormatterProperty()}.\n     *\n     * @return the date and time formatter\n     */\n    public final DateTimeFormatter getDateTimeFormatter() {\n        return dateTimeFormatterProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #dateTimeFormatterProperty()}.\n     *\n     * @param formatter\n     *            the date and time formatter\n     */\n    public final void setDateTimeFormatter(DateTimeFormatter formatter) {\n        requireNonNull(formatter);\n        dateTimeFormatterProperty().set(formatter);\n    }\n\n    private final BooleanProperty showDate = new SimpleBooleanProperty(this,\n            \"showDate\", true);\n\n    /**\n     * Determines whether the date will be shown by the page in the upper right\n     * corner.\n     *\n     * @return true if the date will be shown\n     */\n    public final BooleanProperty showDateProperty() {\n        return showDate;\n    }\n\n    /**\n     * Sets the value of {@link #showDateProperty()}.\n     *\n     * @param show\n     *            if true the date willl be shown\n     */\n    public final void setShowDate(boolean show) {\n        showDateProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showDateProperty()}.\n     *\n     * @return true if the date will be shown\n     */\n    public final boolean isShowDate() {\n        return showDateProperty().get();\n    }\n\n    private final BooleanProperty showNavigation = new SimpleBooleanProperty(this, \"showNavigation\", true);\n\n    /**\n     * Determines if the navigation controls for going back and forward in time\n     * will be shown in the upper right corner.\n     *\n     * @return true if the navigation controls will be shown\n     */\n    public final BooleanProperty showNavigationProperty() {\n        return showNavigation;\n    }\n\n    /**\n     * Sets the value of {@link #showNavigationProperty()}.\n     *\n     * @param show\n     *            if true the navigation controls will be shown\n     */\n    public final void setShowNavigation(boolean show) {\n        showNavigationProperty().set(show);\n    }\n\n    /**\n     * Returns the value of {@link #showNavigationProperty()}.\n     *\n     * @return true if the navigation controls will be shown\n     */\n    public final boolean isShowNavigation() {\n        return showNavigationProperty().get();\n    }\n\n    /**\n     * Returns the type of view used when printing this page.\n     *\n     * @return The print view type.\n     */\n    public abstract ViewType getPrintViewType();\n\n    private final String PAGE_BASE_CATEGORY = \"Page Base\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showNavigationProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowNavigation((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowNavigation();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Navigation\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Navigation controls (back, forward, today)\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PAGE_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showDateProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowDate((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowDate();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Show Date\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Header with current month, day, or year.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PAGE_BASE_CATEGORY;\n            }\n        });\n\n        items.add(new Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(dateTimeFormatterProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setDateTimeFormatter((DateTimeFormatter) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getDateTimeFormatter();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return DateTimeFormatter.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Date Time Formatter\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Date time formatter\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PAGE_BASE_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/WeekPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.page;\n\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.WeekDayView;\nimport com.calendarfx.view.WeekTimeScaleView;\nimport com.calendarfx.view.print.ViewType;\nimport impl.com.calendarfx.view.page.WeekPageSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.HBox;\nimport org.controlsfx.control.PropertySheet;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.time.format.DateTimeFormatter;\nimport java.util.Optional;\n\n/**\n * A composite view focused on displaying calendar information for several days\n * in a row, normally a week. The view consists of the page \"chrome\" inherited\n * from the superclass, a {@link WeekDayView} for each day, an\n * {@link AllDayView}, and a {@link WeekTimeScaleView}.\n *\n * <img width=\"100%\" src=\"doc-files/week-page.png\" alt=\"Week Page\">\n */\npublic class WeekPage extends PageBase {\n\n    private final DetailedWeekView detailedWeekView;\n\n    private final HBox toolBarControls = new HBox();\n\n    /**\n     * Constructs a new week page.\n     */\n    public WeekPage() {\n        getStyleClass().add(\"week-page\");\n        setDateTimeFormatter(DateTimeFormatter.ofPattern(Messages.getString(\"WeekPage.DATE_FORMAT\")));\n\n        this.toolBarControls.getStyleClass().add(\"toolbar-controls-box\");\n        this.detailedWeekView = new DetailedWeekView();\n\n        FontIcon layoutIcon = new FontIcon(FontAwesome.TABLE);\n        layoutIcon.getStyleClass().addAll(\"button-icon\", \"layout-button-icon\");\n\n        ToggleButton layoutButton = new ToggleButton();\n        layoutButton.setMaxHeight(Double.MAX_VALUE);\n        layoutButton.setTooltip(new Tooltip(Messages.getString(\"WeekPage.TOOLTIP_LAYOUT\")));\n        layoutButton.setId(\"layout-button\");\n        layoutButton.setGraphic(layoutIcon);\n        layoutButton.setSelected(getLayout().equals(Layout.SWIMLANE));\n        layoutButton.setOnAction(evt -> {\n            if (layoutButton.isSelected()) {\n                setLayout(Layout.SWIMLANE);\n            } else {\n                setLayout(Layout.STANDARD);\n            }\n        });\n\n        showLayoutButtonProperty().addListener(it -> updateToolBarControls(layoutButton));\n\n        updateToolBarControls(layoutButton);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new WeekPageSkin(this);\n    }\n\n    @Override\n    public Node getToolBarControls() {\n        return toolBarControls;\n    }\n\n    private void updateToolBarControls(ToggleButton layoutButton) {\n        if (isShowLayoutButton()) {\n            toolBarControls.getChildren().setAll(layoutButton);\n        } else {\n            toolBarControls.getChildren().clear();\n        }\n    }\n\n    private final BooleanProperty showLayoutButton = new SimpleBooleanProperty(this, \"showLayoutButton\", true);\n\n    /**\n     * Controls whether the \"layout\" button (to toggle between standard and\n     * swimlane layout) in the upper left corner of the control will be shown to\n     * the user or not.\n     *\n     * @return true if the \"print\" button will be accessible by the user\n     */\n    public final BooleanProperty showLayoutButtonProperty() {\n        return showLayoutButton;\n    }\n\n    /**\n     * Returns the value of {@link #showLayoutButtonProperty()}.\n     *\n     * @return true if the \"layout\" button will be accessible by the user\n     */\n    public final boolean isShowLayoutButton() {\n        return showLayoutButton.get();\n    }\n\n    /**\n     * Sets the value of {@link #showLayoutButtonProperty()}.\n     *\n     * @param show if true the \"layout\" button will be accessible by the user\n     */\n    public final void setShowLayoutButton(boolean show) {\n        showLayoutButton.set(show);\n    }\n\n    /**\n     * Returns the week view child control.\n     *\n     * @return the week view\n     */\n    public final DetailedWeekView getDetailedWeekView() {\n        return detailedWeekView;\n    }\n\n    @Override\n    public final void goForward() {\n        setDate(detailedWeekView.getStartDate().plusDays(\n                Math.max(7, detailedWeekView.getNumberOfDays())));\n    }\n\n    @Override\n    public final void goBack() {\n        setDate(detailedWeekView.getStartDate().minusDays(Math.max(7, detailedWeekView.getNumberOfDays())));\n    }\n\n    @Override\n    public final ViewType getPrintViewType() {\n        return ViewType.WEEK_VIEW;\n    }\n\n    private final String WEEK_PAGE_CATEGORY = \"Week Page\";\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showLayoutButtonProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setShowLayoutButton((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowLayoutButton();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Layout Button\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Can the user access the button to toggle the layout or not.\";\n            }\n\n            @Override\n            public String getCategory() {\n                return WEEK_PAGE_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/YearPage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.page;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.MonthSheetView;\nimport com.calendarfx.view.MonthSheetView.ClickBehaviour;\nimport com.calendarfx.view.YearView;\nimport com.calendarfx.view.print.ViewType;\nimport impl.com.calendarfx.view.page.YearPageSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.Skin;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.Tooltip;\nimport org.controlsfx.control.PropertySheet;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.time.format.DateTimeFormatter;\nimport java.util.Optional;\n\n/**\n * A composite view focused on displaying calendar information for a single\n * year. The view consists of the page \"chrome\" inherited from the superclass\n * and a view of type {@link YearView}. Alternatively the page can\n * also display months in columns via the {@link MonthSheetView} control. The\n * application can switch between these two views by calling {@link #setDisplayMode(DisplayMode)}.\n *\n * <h3>YearView</h3>\n * <img width=\"100%\" src=\"doc-files/year-page.png\" alt=\"Year Page\">\n * <h3>MonthSheetView</h3>\n * <img width=\"100%\" src=\"doc-files/year-page-2.png\" alt=\"Year Page 2\">\n */\npublic class YearPage extends PageBase {\n\n    private final YearView yearView;\n    private final MonthSheetView monthSheetView;\n    private final ToggleButton displayModeButton;\n\n    /**\n     * Constructs a new year page.\n     */\n    public YearPage() {\n        getStyleClass().add(\"year-page\");\n\n        this.yearView = new YearView();\n\n        this.monthSheetView = new MonthSheetView();\n        this.monthSheetView.setCellFactory(param -> new MonthSheetView.DetailedDateCell(param.getView(), param.getDate()));\n        this.monthSheetView.setClickBehaviour(ClickBehaviour.SHOW_DETAILS);\n\n        bind(yearView, true);\n        bind(monthSheetView, true);\n\n        Bindings.bindBidirectional(monthSheetView.showTodayProperty(), showTodayProperty());\n\n        setDateTimeFormatter(DateTimeFormatter.ofPattern(Messages.getString(\"YearPage.DATE_FORMAT\")));\n\n        displayModeProperty().addListener(it -> updateDisplayModeIcon());\n\n        displayModeButton = new ToggleButton();\n        displayModeButton.setMaxHeight(Double.MAX_VALUE);\n        displayModeButton.setId(\"display-mode-button\");\n        displayModeButton.setTooltip(new Tooltip(Messages.getString(\"YearPage.TOOLTIP_DISPLAY_MODE\")));\n        displayModeButton.setSelected(getDisplayMode().equals(DisplayMode.COLUMNS));\n        displayModeButton.selectedProperty().addListener(it -> {\n            if (displayModeButton.isSelected()) {\n                setDisplayMode(DisplayMode.COLUMNS);\n            } else {\n                setDisplayMode(DisplayMode.GRID);\n            }\n        });\n\n        displayModeProperty().addListener(it -> displayModeButton.setSelected(getDisplayMode().equals(DisplayMode.COLUMNS)));\n\n        updateDisplayModeIcon();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new YearPageSkin(this);\n    }\n\n    @Override\n    public Node getToolBarControls() {\n        return displayModeButton;\n    }\n\n    /*\n     * Sets the graphic node on the display mode button based on the current display\n     * mode (column or grid).\n     */\n    private void updateDisplayModeIcon() {\n        FontAwesome icon = FontAwesome.CALENDAR;\n        if (getDisplayMode().equals(DisplayMode.GRID)) {\n            icon = FontAwesome.TABLE;\n        }\n\n        final FontIcon graphic = new FontIcon(icon);\n        graphic.getStyleClass().addAll(\"button-icon\", \"display-mode-icon\");\n        displayModeButton.setGraphic(graphic);\n    }\n\n    /**\n     * An enum used for setting the display mode of the {@link YearPage}. The\n     * page can display the 12 months of a year in a grid or a column layout.\n     *\n     * @see #displayModeProperty()\n     */\n    public enum DisplayMode {\n        GRID,\n        COLUMNS\n    }\n\n    private final ObjectProperty<DisplayMode> displayMode = new SimpleObjectProperty<>(this, \"displayMode\", DisplayMode.GRID);\n\n    /**\n     * A property used to control whether the page should display the year in a grid format (with\n     * each cell containing a single month) or in a column format (each column representing one\n     * month).\n     *\n     * @return the display mode\n     */\n    public final ObjectProperty<DisplayMode> displayModeProperty() {\n        return displayMode;\n    }\n\n    /**\n     * Returns the value of {@link #displayModeProperty()}.\n     *\n     * @return the current display mode\n     */\n    public final DisplayMode getDisplayMode() {\n        return displayMode.get();\n    }\n\n    /**\n     * Sets the value of {@link #displayModeProperty()}.\n     *\n     * @param mode the display mode\n     */\n    public final void setDisplayMode(DisplayMode mode) {\n        this.displayMode.set(mode);\n    }\n\n    /**\n     * Returns the {@link MonthSheetView} used by the page to display months\n     * in columns.\n     *\n     * @return the month sheet view\n     */\n    public final MonthSheetView getMonthSheetView() {\n        return monthSheetView;\n    }\n\n\n    /**\n     * Returns the {@link YearView} used by the page to display months\n     * in a 4x3 grid layout.\n     *\n     * @return the year view\n     */\n    public final YearView getYearView() {\n        return yearView;\n    }\n\n    @Override\n    public final void goForward() {\n        setDate(getDate().plusYears(1));\n    }\n\n    @Override\n    public final void goBack() {\n        setDate(getDate().minusYears(1));\n    }\n\n    @Override\n    public final ViewType getPrintViewType() {\n        /*\n        We currently do not support printing years, hence we return MONTH_VIEW.\n         */\n        return ViewType.MONTH_VIEW;\n    }\n\n    private final String YEAR_PAGE_CATEGORY = \"Year Page\";\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(displayModeProperty());\n            }\n\n            @Override\n            public void setValue(Object value) {\n                setDisplayMode((DisplayMode) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getDisplayMode();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return DisplayMode.class;\n            }\n\n            @Override\n            public String getName() {\n                return \"Display Mode\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Grid or Column Layout\";\n            }\n\n            @Override\n            public String getCategory() {\n                return YEAR_PAGE_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/page/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * Complex controls that wire several date controls together for a rich\n * user experience.\n */\npackage com.calendarfx.view.page;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/DatePopOver.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport javafx.scene.control.Label;\nimport org.controlsfx.control.PopOver;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A popover that shows the calendar entries for a given date.\n *\n * <img src=\"doc-files/date-popover.png\" alt=\"Date PopOver\">\n */\npublic class DatePopOver extends PopOver {\n\n    private final DateControl dateControl;\n    private final LocalDate date;\n\n    /**\n     * Constructs a new popover for the given date.\n     *\n     * @param control the date control where the popover is being used\n     * @param date the date for which to show the entries\n     */\n    public DatePopOver(DateControl control, LocalDate date) {\n        this.dateControl = requireNonNull(control);\n        this.date = requireNonNull(date);\n        EntriesPane entriesPane = new EntriesPane(control);\n\n        getRoot().getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n        getRoot().getStyleClass().add(\"root\");\n\n        List<Entry<?>> entries = findEntries();\n        if (entries.isEmpty()) {\n            Label label = new Label();\n            label.setText(\"No Entries\");\n            label.getStyleClass().add(\"no-entries-label\");\n            setContentNode(label);\n        } else {\n            entriesPane.getEntries().setAll(entries);\n            setContentNode(entriesPane);\n        }\n\n        getStyleClass().add(\"date-popover\");\n        setArrowIndent(4);\n        setDetachable(false);\n        setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER);\n        setCornerRadius(4);\n    }\n\n    /**\n     * Returns the date control where the popover is being used.\n     *\n     * @return the date control\n     */\n    public final DateControl getDateControl() {\n        return dateControl;\n    }\n\n    /**\n     * Returns the date where the popover is being used.\n     *\n     * @return the date\n     */\n    public final LocalDate getDate() {\n        return date;\n    }\n\n    private List<Entry<?>> findEntries() {\n        List<Entry<?>> result = new ArrayList<>();\n        for (CalendarSource source : dateControl.getCalendarSources()) {\n            for (Calendar calendar : source.getCalendars()) {\n                try {\n                    Map<LocalDate, List<Entry<?>>> entriesMap = calendar.findEntries(date, date, dateControl.getZoneId());\n                    List<Entry<?>> entriesList = entriesMap.get(date);\n                    if (entriesList != null) {\n                        result.addAll(entriesList);\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        Collections.sort(result);\n        return result;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntriesPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.Messages;\nimport javafx.beans.Observable;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.shape.Circle;\n\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.time.format.TextStyle;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\n\nclass EntriesPane extends VBox {\n\n    private final DateControl dateControl;\n\n    public EntriesPane(DateControl dateControl) {\n        this.dateControl = Objects.requireNonNull(dateControl, \"date control can not be null\");\n\n        setMinSize(0, 0);\n        getEntries().addListener((Observable evt) -> update());\n        getStyleClass().add(\"entries-pane\");\n        setAlignment(Pos.CENTER);\n    }\n\n    private final ObservableList<Entry<?>> entries = FXCollections.observableArrayList();\n\n    public final ObservableList<Entry<?>> getEntries() {\n        return entries;\n    }\n\n    private void update() {\n\n        if (!entries.isEmpty()) {\n\n            List<Entry<?>> workingList = new ArrayList<>(entries);\n\n            /*\n             * Individual calendars are already sorted, but now we are\n             * displaying entries from several calendars, so let's resort.\n             */\n            Collections.sort(workingList);\n\n            for (Entry<?> entry : workingList) {\n                Calendar calendar = entry.getCalendar();\n\n                BorderPane borderPane = new BorderPane();\n                borderPane.getStyleClass().add(\"entry\");\n\n                Label titleLabel = new Label(entry.getTitle());\n                BorderPane.setAlignment(titleLabel, Pos.CENTER_LEFT);\n                titleLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n                titleLabel.getStyleClass().add(\"title\");\n                borderPane.setCenter(titleLabel);\n\n                Circle colorDot = new Circle();\n                colorDot.setRadius(2.5);\n                colorDot.getStyleClass().add(calendar.getStyle() + \"-icon-small\");\n                titleLabel.setGraphic(colorDot);\n\n                Label timeLabel = new Label();\n                if (entry.isFullDay()) {\n                    timeLabel.setText(Messages.getString(\"EntriesPane.FULL_DAY\"));\n                } else {\n\n                    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n                    ZonedDateTime startTime = entry.getStartAsZonedDateTime();\n                    ZoneId entryZoneId = entry.getZoneId();\n                    ZoneId controlZoneId = dateControl.getZoneId();\n\n                    if (!Objects.equals(entryZoneId, controlZoneId)) {\n                        timeLabel.setText(formatter.format(startTime.withZoneSameInstant(controlZoneId)) + \" (\" + formatter.format(startTime) + \" \" + entryZoneId.getDisplayName(TextStyle.SHORT, Locale.getDefault())+ \")\");\n                    } else {\n                        timeLabel.setText(formatter.format(startTime));\n                    }\n                }\n\n                borderPane.setRight(timeLabel);\n\n                timeLabel.getStyleClass().add(\"time\");\n                BorderPane.setAlignment(timeLabel, Pos.CENTER_RIGHT);\n\n                getChildren().add(borderPane);\n            }\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryDetailsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.RecurrenceView;\nimport com.calendarfx.view.TimeField;\nimport impl.com.calendarfx.view.ZoneIdStringConverter;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.transformation.SortedList;\nimport javafx.geometry.HPos;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.MenuButton;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\n\nimport java.time.ZoneId;\n\npublic class EntryDetailsView extends EntryPopOverPane {\n\n    private final Label summaryLabel;\n    private final MenuButton recurrenceButton;\n    private final TimeField startTimeField = new TimeField();\n    private final TimeField endTimeField = new TimeField();\n    private final DatePicker startDatePicker = new DatePicker();\n    private final DatePicker endDatePicker = new DatePicker();\n    private final ComboBox<ZoneId> zoneBox = new ComboBox<>();\n    private final Entry<?> entry;\n\n    private boolean updatingFields;\n\n    private final InvalidationListener entryIntervalListener = it -> {\n        updatingFields = true;\n        try {\n            Entry<?> entry = getEntry();\n            startTimeField.setValue(entry.getStartTime());\n            endTimeField.setValue(entry.getEndTime());\n            startDatePicker.setValue(entry.getStartDate());\n            endDatePicker.setValue(entry.getEndDate());\n            zoneBox.setValue(entry.getZoneId());\n        } finally {\n            updatingFields = false;\n        }\n    };\n\n    private final WeakInvalidationListener weakEntryIntervalListener = new WeakInvalidationListener(entryIntervalListener);\n\n    private final InvalidationListener recurrenceRuleListener = it -> updateRecurrenceRuleButton(getEntry());\n\n    private final WeakInvalidationListener weakRecurrenceRuleListener = new WeakInvalidationListener(recurrenceRuleListener);\n\n    private final InvalidationListener updateSummaryLabelListener = it -> updateSummaryLabel(getEntry());\n\n    private final WeakInvalidationListener weakUpdateSummaryLabelListener = new WeakInvalidationListener(updateSummaryLabelListener);\n\n    public EntryDetailsView(Entry<?> entry, DateControl dateControl) {\n        super();\n\n        this.entry = entry;\n\n        getStyleClass().add(\"entry-details-view\");\n\n        Label fullDayLabel = new Label(Messages.getString(\"EntryDetailsView.FULL_DAY\"));\n        Label startDateLabel = new Label(Messages.getString(\"EntryDetailsView.FROM\"));\n        Label endDateLabel = new Label(Messages.getString(\"EntryDetailsView.TO\"));\n        Label recurrentLabel = new Label(Messages.getString(\"EntryDetailsView.REPEAT\"));\n\n        summaryLabel = new Label();\n        summaryLabel.getStyleClass().add(\"recurrence-summary-label\");\n        summaryLabel.setWrapText(true);\n        summaryLabel.setMaxWidth(300);\n\n        CheckBox fullDay = new CheckBox();\n        fullDay.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        startTimeField.setValue(entry.getStartTime());\n        startTimeField.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        endTimeField.setValue(entry.getEndTime());\n        endTimeField.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        startDatePicker.setValue(entry.getStartDate());\n        startDatePicker.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        endDatePicker.setValue(entry.getEndDate());\n        endDatePicker.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        entry.intervalProperty().addListener(weakEntryIntervalListener);\n\n        HBox startDateBox = new HBox(10);\n        HBox endDateBox = new HBox(10);\n\n        startDateBox.setAlignment(Pos.CENTER_LEFT);\n        endDateBox.setAlignment(Pos.CENTER_LEFT);\n\n        startDateBox.getChildren().addAll(startDateLabel, startDatePicker, startTimeField);\n        endDateBox.getChildren().addAll(endDateLabel, endDatePicker, endTimeField);\n\n        fullDay.setSelected(entry.isFullDay());\n        startDatePicker.setValue(entry.getStartDate());\n        endDatePicker.setValue(entry.getEndDate());\n\n        Label zoneLabel = new Label(Messages.getString(\"EntryDetailsView.TIMEZONE\"));\n        zoneLabel.visibleProperty().bind(dateControl.enableTimeZoneSupportProperty());\n        zoneLabel.managedProperty().bind(dateControl.enableTimeZoneSupportProperty());\n\n        SortedList<ZoneId> sortedZones = new SortedList<>(dateControl.getAvailableZoneIds());\n        sortedZones.setComparator(new ZoneIdComparator());\n\n        zoneBox.setItems(sortedZones);\n        zoneBox.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n        zoneBox.setConverter(new ZoneIdStringConverter());\n        zoneBox.setValue(entry.getZoneId());\n        zoneBox.visibleProperty().bind(dateControl.enableTimeZoneSupportProperty());\n        zoneBox.managedProperty().bind(dateControl.enableTimeZoneSupportProperty());\n\n        recurrenceButton = new MenuButton(Messages.getString(\"EntryDetailsView.MENU_BUTTON_NONE\"));\n\n        MenuItem none = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_NONE\"));\n        MenuItem everyDay = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_EVERY_DAY\"));\n        MenuItem everyWeek = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_EVERY_WEEK\"));\n        MenuItem everyMonth = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_EVERY_MONTH\"));\n        MenuItem everyYear = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_EVERY_YEAR\"));\n        MenuItem custom = new MenuItem(Messages.getString(\"EntryDetailsView.MENU_ITEM_CUSTOM\"));\n\n        none.setOnAction(evt -> updateRecurrenceRule(entry, null));\n        everyDay.setOnAction(evt -> updateRecurrenceRule(entry, \"RRULE:FREQ=DAILY\"));\n        everyWeek.setOnAction(evt -> updateRecurrenceRule(entry, \"RRULE:FREQ=WEEKLY\"));\n        everyMonth.setOnAction(evt -> updateRecurrenceRule(entry, \"RRULE:FREQ=MONTHLY\"));\n        everyYear.setOnAction(evt -> updateRecurrenceRule(entry, \"RRULE:FREQ=YEARLY\"));\n        custom.setOnAction(evt -> showRecurrenceEditor(entry));\n\n        recurrenceButton.getItems().setAll(none, everyDay, everyWeek, everyMonth, everyYear, new SeparatorMenuItem(), custom);\n        recurrenceButton.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        EntryMapView mapView = new EntryMapView(entry);\n\n        GridPane box = new GridPane();\n        box.getStyleClass().add(\"content\");\n        box.add(fullDayLabel, 0, 0);\n        box.add(fullDay, 1, 0);\n        box.add(startDateLabel, 0, 1);\n        box.add(startDateBox, 1, 1);\n        box.add(endDateLabel, 0, 2);\n        box.add(endDateBox, 1, 2);\n        box.add(zoneLabel, 0, 3);\n        box.add(zoneBox, 1, 3);\n        box.add(recurrentLabel, 0, 4);\n        box.add(recurrenceButton, 1, 4);\n        box.add(summaryLabel, 1, 5);\n        box.add(mapView, 1, 6);\n\n        GridPane.setFillWidth(zoneBox, true);\n        GridPane.setHgrow(zoneBox, Priority.ALWAYS);\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        ColumnConstraints col2 = new ColumnConstraints();\n\n        col1.setHalignment(HPos.RIGHT);\n        col2.setHalignment(HPos.LEFT);\n\n        box.getColumnConstraints().addAll(col1, col2);\n\n        getChildren().add(box);\n\n        startTimeField.visibleProperty().bind(Bindings.not(entry.fullDayProperty()));\n        endTimeField.visibleProperty().bind(Bindings.not(entry.fullDayProperty()));\n\n        // start date and time\n        startDatePicker.valueProperty().addListener((obs, oldValue, newValue) -> {\n            if (!updatingFields) {\n                // Work-Around for DatePicker bug introduced with 18+9 (\"commit on focus lost\").\n                startDatePicker.getEditor().setText(startDatePicker.getConverter().toString(newValue));\n                entry.changeStartDate(newValue, true);\n            }\n        });\n\n        startTimeField.valueProperty().addListener((obs, oldValue, newValue) -> {\n            if (!updatingFields) {\n                entry.changeStartTime(newValue, true);\n            }\n        });\n\n        // end date and time\n        endDatePicker.valueProperty().addListener((obs, oldValue, newValue) -> {\n            if (!updatingFields) {\n                // Work-Around for DatePicker bug introduced with 18+9 (\"commit on focus lost\").\n                endDatePicker.getEditor().setText(endDatePicker.getConverter().toString(newValue));\n                entry.changeEndDate(newValue, false);\n            }\n        });\n\n        endTimeField.valueProperty().addListener((obs, oldValue, newValue) -> {\n            if (!updatingFields) {\n                entry.changeEndTime(newValue, false);\n            }\n        });\n\n        zoneBox.valueProperty().addListener((obs, oldValue, newValue) -> {\n            if (!updatingFields && zoneBox.getValue() != null) {\n                entry.changeZoneId(newValue);\n            }\n        });\n\n        // full day\n        fullDay.setOnAction(evt -> entry.setFullDay(fullDay.isSelected()));\n\n        entry.recurrenceRuleProperty().addListener(weakRecurrenceRuleListener);\n\n        updateRecurrenceRuleButton(entry);\n        updateSummaryLabel(entry);\n\n        entry.recurrenceRuleProperty().addListener(weakUpdateSummaryLabelListener);\n    }\n\n    public final Entry<?> getEntry() {\n        return entry;\n    }\n\n    private void updateSummaryLabel(Entry<?> entry) {\n        String rule = entry.getRecurrenceRule();\n        if (rule != null && !rule.trim().equals(\"\")) {\n            String text = Util.convertRFC2445ToText(rule, entry.getStartDate());\n            summaryLabel.setText(text);\n            summaryLabel.setVisible(true);\n            summaryLabel.setManaged(true);\n        } else {\n            summaryLabel.setText(\"\");\n            summaryLabel.setVisible(false);\n            summaryLabel.setManaged(false);\n        }\n    }\n\n    private void showRecurrenceEditor(Entry<?> entry) {\n        RecurrencePopup popup = new RecurrencePopup();\n        RecurrenceView recurrenceView = popup.getRecurrenceView();\n        String recurrenceRule = entry.getRecurrenceRule();\n        if (recurrenceRule == null || recurrenceRule.trim().equals(\"\")) {\n            recurrenceRule = \"RRULE:FREQ=DAILY;\";\n        }\n        recurrenceView.setRecurrenceRule(recurrenceRule);\n        popup.setOnOkPressed(evt -> {\n            String rrule = recurrenceView.getRecurrenceRule();\n            entry.setRecurrenceRule(rrule);\n        });\n\n        Point2D anchor = recurrenceButton.localToScreen(0, recurrenceButton.getHeight());\n        popup.show(recurrenceButton, anchor.getX(), anchor.getY());\n    }\n\n    private void updateRecurrenceRule(Entry<?> entry, String rule) {\n        entry.setRecurrenceRule(rule);\n    }\n\n    private void updateRecurrenceRuleButton(Entry<?> entry) {\n        String rule = entry.getRecurrenceRule();\n        if (rule == null) {\n            recurrenceButton.setText(Messages.getString(\"EntryDetailsView.NONE\"));\n        } else {\n            switch (rule.trim().toUpperCase()) {\n                case \"RRULE:FREQ=DAILY\":\n                    recurrenceButton.setText(Messages.getString(\"EntryDetailsView.DAILY\"));\n                    break;\n                case \"RRULE:FREQ=WEEKLY\":\n                    recurrenceButton.setText(Messages.getString(\"EntryDetailsView.WEEKLY\"));\n                    break;\n                case \"RRULE:FREQ=MONTHLY\":\n                    recurrenceButton.setText(Messages.getString(\"EntryDetailsView.MONTHLY\"));\n                    break;\n                case \"RRULE:FREQ=YEARLY\":\n                    recurrenceButton.setText(Messages.getString(\"EntryDetailsView.YEARLY\"));\n                    break;\n                default:\n                    recurrenceButton.setText(Messages.getString(\"EntryDetailsView.CUSTOM\"));\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryHeaderView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarSelector;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.Messages;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.geometry.VPos;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.RowConstraints;\n\nimport java.util.List;\n\nimport static java.util.Objects.requireNonNull;\n\npublic class EntryHeaderView extends GridPane {\n\n    private final CalendarSelector calendarSelector;\n\n    private final Entry<?> entry;\n\n    private final TextField titleField = new TextField();\n\n    private final ChangeListener<Calendar> calendarChangeListener = (observable, oldCalendar, newCalendar) -> {\n        if (oldCalendar != null) {\n            titleField.getStyleClass().remove(oldCalendar.getStyle() + \"-entry-popover-title\");\n        }\n        if (newCalendar != null) {\n            titleField.getStyleClass().add(newCalendar.getStyle() + \"-entry-popover-title\");\n        }\n    };\n\n    private final WeakChangeListener<Calendar> weakCalendarChangeListener = new WeakChangeListener<>(calendarChangeListener);\n\n    public EntryHeaderView(Entry<?> entry, List<Calendar> calendars) {\n        this.entry = requireNonNull(entry);\n        requireNonNull(calendars);\n\n        Bindings.bindBidirectional(titleField.textProperty(), entry.titleProperty());\n\n        titleField.setText(entry.getTitle());\n        titleField.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        TextField locationField = new TextField(entry.getLocation());\n        Bindings.bindBidirectional(locationField.textProperty(), entry.locationProperty());\n        locationField.getStyleClass().add(\"location\");\n        locationField.setEditable(true);\n        locationField.setPromptText(Messages.getString(\"EntryHeaderView.PROMPT_LOCATION\"));\n        locationField.setMaxWidth(500);\n        locationField.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n\n        calendarSelector = new CalendarSelector();\n        calendarSelector.disableProperty().bind(entry.getCalendar().readOnlyProperty());\n        calendarSelector.getCalendars().setAll(calendars);\n        calendarSelector.setCalendar(entry.getCalendar());\n        Bindings.bindBidirectional(calendarSelector.calendarProperty(), entry.calendarProperty());\n\n        titleField.getStyleClass().add(\"default-style-entry-popover-title\");\n\n        add(titleField, 0, 0);\n        add(calendarSelector, 1, 0, 1, 2);\n        add(locationField, 0, 1);\n\n        RowConstraints row1 = new RowConstraints();\n        row1.setValignment(VPos.TOP);\n        row1.setFillHeight(true);\n\n        RowConstraints row2 = new RowConstraints();\n        row2.setValignment(VPos.TOP);\n        row2.setFillHeight(true);\n\n        getRowConstraints().addAll(row1, row2);\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        col1.setFillWidth(true);\n        col1.setHgrow(Priority.ALWAYS);\n\n        ColumnConstraints col2 = new ColumnConstraints();\n        col2.setFillWidth(true);\n        col2.setHgrow(Priority.NEVER);\n\n        getColumnConstraints().addAll(col1, col2);\n\n        getStyleClass().add(\"popover-header\");\n\n        titleField.getStyleClass().add(\"title\");\n        titleField.setPromptText(Messages.getString(\"EntryHeaderView.PROMPT_TITLE\"));\n        titleField.setMaxWidth(500);\n\n        Calendar calendar = entry.getCalendar();\n\n        titleField.getStyleClass().add(calendar.getStyle() + \"-entry-popover-title\");\n\n        entry.calendarProperty().addListener(weakCalendarChangeListener);\n    }\n\n    /**\n     * Returns the currently selected calendar.\n     *\n     * @return the selected calendar\n     */\n    public final Calendar getCalendar() {\n        Calendar calendar = calendarSelector.getCalendar();\n        if (calendar == null) {\n            calendar = entry.getCalendar();\n        }\n\n        return calendar;\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryMapView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.concurrent.Service;\nimport javafx.concurrent.Task;\nimport javafx.geometry.Insets;\nimport javafx.scene.Cursor;\nimport javafx.scene.image.Image;\nimport javafx.scene.layout.Background;\nimport javafx.scene.layout.BackgroundImage;\nimport javafx.scene.layout.BackgroundPosition;\nimport javafx.scene.layout.BackgroundRepeat;\nimport javafx.scene.layout.BackgroundSize;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.shape.Rectangle;\n\nimport java.awt.Desktop;\nimport java.awt.Desktop.Action;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Objects;\nimport java.util.logging.Level;\n\n/**\n * Pane that shows in a map the location of the entry.\n */\npublic class EntryMapView extends EntryPopOverPane {\n\n    private final Entry<?> entry;\n\n    private final ImageLoader imageLoader = new ImageLoader();\n\n    private final InvalidationListener invalidationListener = it -> imageLoader.restart();\n\n    private final WeakInvalidationListener weakInvalidationListener = new WeakInvalidationListener(invalidationListener);\n\n    public EntryMapView(Entry<?> entry) {\n        this.entry = Objects.requireNonNull(entry);\n\n        StackPane mapViewPane = new StackPane();\n        mapViewPane.setPrefSize(340, 240);\n        mapViewPane.setPadding(new Insets(1));\n        mapViewPane.setStyle(\"-fx-border-color: gray; -fx-border-radius: 4px;\");\n        mapViewPane.backgroundProperty().bind(Bindings.createObjectBinding(() -> {\n            Image image = imageLoader.getValue();\n            if (image != null) {\n                return new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, new BackgroundSize(100, 100, true, true, false, true)));\n            }\n            return null;\n        }, imageLoader.valueProperty()));\n\n        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Action.BROWSE)) {\n            mapViewPane.setCursor(Cursor.HAND);\n            mapViewPane.setOnMouseClicked(evt -> {\n                Desktop desktop = Desktop.getDesktop();\n                try {\n                    desktop.browse(new URL(\"https://www.google.com/maps/search/?api=1&query=\" + URLEncoder.encode(entry.getLocation(), StandardCharsets.UTF_8)).toURI());\n                } catch (IOException e) {\n                    throw new RuntimeException(e);\n                } catch (URISyntaxException e) {\n                    throw new RuntimeException(e);\n                }\n            });\n        }\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(mapViewPane.widthProperty());\n        clip.heightProperty().bind(mapViewPane.heightProperty());\n        clip.setArcHeight(4);\n        clip.setArcWidth(4);\n        mapViewPane.setClip(clip);\n\n        getChildren().add(mapViewPane);\n\n        entry.locationProperty().addListener(weakInvalidationListener);\n\n        visibleProperty().bind(entry.locationProperty().isNotNull().and(googleApiKeyProperty().isNotNull()));\n        managedProperty().bind(entry.locationProperty().isNotNull().and(googleApiKeyProperty().isNotNull()));\n\n        imageLoader.restart();\n    }\n\n    private class ImageLoader extends Service<Image> {\n        public ImageLoader() {\n        }\n\n        @Override\n        protected Task createTask() {\n            return new ImageTask(entry.getLocation());\n        }\n    }\n\n    private class ImageTask extends Task<Image> {\n\n        private final String location;\n\n        public ImageTask(String location) {\n            this.location = location;\n        }\n\n        @Override\n        protected Image call() throws Exception {\n            Thread.sleep(500);\n\n            if (isCancelled() || getGoogleApiKey().isBlank() || location == null || location.trim().equals(\"\")) {\n                return null;\n            }\n\n            return loadMapImage(location);\n        }\n\n    }\n\n    public Image loadMapImage(String address) {\n        if (address != null) {\n            try {\n                String encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8);\n                String key = getGoogleApiKey();\n                String url = \"https://maps.googleapis.com/maps/api/staticmap?center=\" + encodedAddress + \"&markers=color:0xE94B35%7C\" + encodedAddress + \"&scale=2&zoom=16&size=340x240&key=\" + key;\n                LoggingDomain.VIEW.fine(\"google map call (static image): \" + url);\n                Image image = new Image(url);\n                return image;\n            } catch (Exception ex) {\n                LoggingDomain.VIEW.log(Level.SEVERE, \"error when trying to load google maps static image\", ex);\n            }\n        }\n\n        return null;\n    }\n\n    private final StringProperty googleApiKey = new SimpleStringProperty(this, \"googleApiKey\", System.getProperty(\"calendarfx.google.api.key\"));\n\n    public final String getGoogleApiKey() {\n        return googleApiKey.get();\n    }\n\n    public final StringProperty googleApiKeyProperty() {\n        return googleApiKey;\n    }\n\n    public final void setGoogleApiKey(String googleApiKey) {\n        this.googleApiKey.set(googleApiKey);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryPopOverContentPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.Messages;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.util.Duration;\nimport org.controlsfx.control.PopOver;\n\nimport java.util.Objects;\n\npublic class EntryPopOverContentPane extends PopOverContentPane {\n\n    private final Entry<?> entry;\n    private final DateControl dateControl;\n    private final PopOver popOver;\n\n    private final InvalidationListener hideListener = it -> {\n        if (getEntry().getCalendar() == null) {\n            getPopOver().hide(Duration.ZERO);\n        }\n    };\n\n    private final WeakInvalidationListener weakHideListener = new WeakInvalidationListener(hideListener);\n\n    private final InvalidationListener fullDayListener = obs -> {\n        if (getEntry().isFullDay() && !getPopOver().isDetached() && getDateControl() instanceof DayViewBase) {\n            getPopOver().setDetached(true);\n        }\n    };\n\n    private final WeakInvalidationListener weakFullDayListener = new WeakInvalidationListener(fullDayListener);\n\n    public EntryPopOverContentPane(PopOver popOver, DateControl dateControl, Entry<?> entry) {\n        getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n\n        this.popOver = popOver;\n        this.dateControl = dateControl;\n        this.entry = Objects.requireNonNull(entry);\n\n        EntryDetailsView details = new EntryDetailsView(entry, dateControl);\n        PopOverTitledPane detailsPane = new PopOverTitledPane(Messages.getString(\"EntryPopOverContentPane.DETAILS\"), details);\n\n        EntryHeaderView header = new EntryHeaderView(entry, dateControl.getCalendars());\n        setHeader(header);\n\n        if (Boolean.getBoolean(\"calendarfx.developer\")) {\n            EntryPropertiesView properties = new EntryPropertiesView(entry);\n            PopOverTitledPane propertiesPane = new PopOverTitledPane(\"Properties\", properties);\n            propertiesPane.getStyleClass().add(\"no-padding\");\n            getPanes().addAll(detailsPane, propertiesPane);\n        } else {\n            getPanes().addAll(detailsPane);\n        }\n\n        setExpandedPane(detailsPane);\n\n        entry.fullDayProperty().addListener(weakFullDayListener);\n        popOver.setOnHidden(evt -> entry.fullDayProperty().removeListener(weakFullDayListener));\n\n        entry.calendarProperty().addListener(weakHideListener);\n    }\n\n    public final PopOver getPopOver() {\n        return popOver;\n    }\n\n    public final DateControl getDateControl() {\n        return dateControl;\n    }\n\n    public final Entry<?> getEntry() {\n        return entry;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryPopOverPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport javafx.scene.layout.StackPane;\n\npublic abstract class EntryPopOverPane extends StackPane {\n\n    public EntryPopOverPane() {\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/EntryPropertiesView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.model.Entry;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.property.BeanPropertyUtils;\n\n/**\n * Created by lemmi on 20.03.17.\n */\npublic class EntryPropertiesView extends EntryPopOverPane {\n\n    public EntryPropertiesView(Entry<?> entry) {\n        getStyleClass().add(\"no-padding\");\n        PropertySheet propertySheet = new PropertySheet();\n        propertySheet.getItems().setAll(BeanPropertyUtils.getProperties(entry));\n        getChildren().add(propertySheet);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/PopOverContentPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.control.Accordion;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.BorderPane;\n\npublic class PopOverContentPane extends BorderPane {\n\n    public PopOverContentPane() {\n        super();\n\n        topProperty().bind(headerProperty());\n\n        Accordion accordion = new Accordion();\n        accordion.getStyleClass().addAll(\"popover-accordion\", \"dense\");\n        setCenter(accordion);\n\n        Bindings.bindContentBidirectional(getPanes(), accordion.getPanes());\n        Bindings.bindBidirectional(expandedPaneProperty(), accordion.expandedPaneProperty());\n\n        bottomProperty().bind(footerProperty());\n\n        headerProperty().addListener((value, oldNode, newNode) -> {\n            if (newNode != null) {\n                String style = \"popover-header\";\n                if (!newNode.getStyleClass().contains(style)) {\n                    newNode.getStyleClass().add(style);\n                }\n            }\n        });\n\n        footerProperty().addListener((value, oldNode, newNode) -> {\n            if (newNode != null) {\n                String style = \"popover-footer\";\n                if (!newNode.getStyleClass().contains(style)) {\n                    newNode.getStyleClass().add(style);\n                }\n            }\n        });\n    }\n\n    // header support\n\n    private final ObjectProperty<Node> header = new SimpleObjectProperty<>(this, \"header\");\n\n    public final ObjectProperty<Node> headerProperty() {\n        return header;\n    }\n\n    public final Node getHeader() {\n        return headerProperty().get();\n    }\n\n    public final void setHeader(Node node) {\n        headerProperty().set(node);\n    }\n\n    // footer support\n\n    private final ObjectProperty<Node> footer = new SimpleObjectProperty<>(this, \"footer\");\n\n    public final ObjectProperty<Node> footerProperty() {\n        return footer;\n    }\n\n    public final Node getFooter() {\n        return footerProperty().get();\n    }\n\n    public final void setFooter(Node node) {\n        footerProperty().set(node);\n    }\n\n    // panes\n\n    private final ObservableList<TitledPane> panes = FXCollections.observableArrayList();\n\n    public final ObservableList<TitledPane> getPanes() {\n        return panes;\n    }\n\n    // Expanded pane support\n\n    private final ObjectProperty<TitledPane> expandedPane = new SimpleObjectProperty<>(this, \"expandedPane\");\n\n    public final ObjectProperty<TitledPane> expandedPaneProperty() {\n        return expandedPane;\n    }\n\n    public final void setExpandedPane(TitledPane titledPane) {\n        expandedPaneProperty().set(titledPane);\n    }\n\n    public final TitledPane getExpandedPane() {\n        return expandedPane.get();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/PopOverTitledPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport javafx.animation.FadeTransition;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.Node;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.TitledPane;\nimport javafx.util.Duration;\n\nimport static javafx.scene.control.ContentDisplay.TEXT_ONLY;\n\npublic class PopOverTitledPane extends TitledPane {\n\n    public PopOverTitledPane(final String title, final Node detailedContent) {\n        this(title, null, detailedContent);\n    }\n\n    public PopOverTitledPane(final String title, final Node summaryContent, final Node detailedContent) {\n        super(title, detailedContent);\n\n        if (title == null) {\n            throw new IllegalArgumentException(\"title can not be null\");\n        }\n\n        if (detailedContent == null) {\n            throw new IllegalArgumentException(\"detailed content can not be null\");\n        }\n\n        setContentDisplay(TEXT_ONLY);\n        setGraphic(summaryContent);\n\n        expandedProperty().addListener((value, oldExpanded, newExpanded) -> {\n            if (newExpanded) {\n                setContentDisplay(ContentDisplay.TEXT_ONLY);\n                detailedContent.setOpacity(0);\n                FadeTransition fadeInContent = new FadeTransition(getFadingDuration());\n                fadeInContent.setFromValue(0);\n                fadeInContent.setToValue(1);\n                fadeInContent.setNode(detailedContent);\n                fadeInContent.play();\n            } else {\n                if (summaryContent != null) {\n                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n                    summaryContent.setOpacity(0);\n                    FadeTransition fadeInSummary = new FadeTransition(getFadingDuration());\n                    fadeInSummary.setFromValue(0);\n                    fadeInSummary.setToValue(1);\n                    fadeInSummary.setNode(summaryContent);\n                    fadeInSummary.play();\n                }\n            }\n        });\n    }\n\n    private final ObjectProperty<Duration> fadingDuration = new SimpleObjectProperty<>(this, \"fadingDuration\", Duration.seconds(.2));\n\n    public final ObjectProperty<Duration> fadingDurationProperty() {\n        return fadingDuration;\n    }\n\n    public final void setFadingDuration(Duration duration) {\n        fadingDurationProperty().set(duration);\n    }\n\n    public final Duration getFadingDuration() {\n        return fadingDurationProperty().get();\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/RecurrencePopup.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.popover;\n\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.RecurrenceView;\nimport impl.com.calendarfx.view.popover.RecurrencePopupSkin;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\nimport javafx.scene.control.PopupControl;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.StackPane;\n\nimport java.util.Objects;\n\npublic class RecurrencePopup extends PopupControl {\n\n    private static final String DEFAULT_STYLE = \"recurrence-popup\";\n\n    private final RecurrenceView recurrenceView;\n\n    private final StackPane root;\n\n    public RecurrencePopup() {\n        getStyleClass().add(DEFAULT_STYLE);\n\n        root = new StackPane();\n        root.getStyleClass().add(\"stackpane\");\n\n        ownerWindowProperty().addListener((obs, oldWindow, newWindow) -> {\n            if (Boolean.getBoolean(\"atlantafx\")) {\n                newWindow.getScene().getStylesheets().add(Objects.requireNonNull(CalendarView.class.getResource(\"atlantafx.css\")).toExternalForm());\n            } else {\n                newWindow.getScene().getStylesheets().add(Objects.requireNonNull(CalendarView.class.getResource(\"calendar.css\")).toExternalForm());\n            }\n        });\n\n        recurrenceView = new RecurrenceView();\n        recurrenceView.setShowSummary(false);\n\n        if (Boolean.getBoolean(\"atlantafx\")) {\n            recurrenceView.getStylesheets().add(Objects.requireNonNull(CalendarView.class.getResource(\"atlantafx.css\")).toExternalForm());\n        } else {\n            recurrenceView.getStylesheets().add(Objects.requireNonNull(CalendarView.class.getResource(\"calendar.css\")).toExternalForm());\n        }\n\n        Bindings.bindContentBidirectional(root.getStyleClass(), getStyleClass());\n\n        setAutoFix(true);\n        setAutoHide(false);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new RecurrencePopupSkin(this);\n    }\n\n\n    public final StackPane getRoot() {\n        return root;\n    }\n\n    public final RecurrenceView getRecurrenceView() {\n        return recurrenceView;\n    }\n\n    private class RecurrencePopupEventHandlerProperty extends SimpleObjectProperty<EventHandler<RecurrencePopupEvent>> {\n\n        private final EventType<RecurrencePopupEvent> eventType;\n\n        public RecurrencePopupEventHandlerProperty(final String name,\n                                                   final EventType<RecurrencePopupEvent> eventType) {\n            super(RecurrencePopup.this, name);\n            this.eventType = eventType;\n        }\n\n        @Override\n        protected void invalidated() {\n            setEventHandler(eventType, get());\n        }\n    }\n\n    /*\n     * OK pressed event support.\n     */\n\n    private RecurrencePopupEventHandlerProperty onOkPressed;\n\n    public final ObjectProperty<EventHandler<RecurrencePopupEvent>> onOkPressedProperty() {\n        if (onOkPressed == null) {\n            onOkPressed = new RecurrencePopupEventHandlerProperty(\"onOkPressed\", RecurrencePopupEvent.OK_PRESSED);\n        }\n\n        return onOkPressed;\n    }\n\n    public final void setOnOkPressed(EventHandler<RecurrencePopupEvent> value) {\n        onOkPressedProperty().set(value);\n    }\n\n    public final EventHandler<RecurrencePopupEvent> getOnOkPressed() {\n        return onOkPressed == null ? null : onOkPressedProperty().get();\n    }\n\n    /*\n     * Cancel pressed event support.\n     */\n\n    private RecurrencePopupEventHandlerProperty onCancelPressed;\n\n    public final ObjectProperty<EventHandler<RecurrencePopupEvent>> onCancelPressedProperty() {\n        if (onCancelPressed == null) {\n            onCancelPressed = new RecurrencePopupEventHandlerProperty(\n                    \"onCancelPressed\", RecurrencePopupEvent.CANCEL_PRESSED);\n        }\n\n        return onCancelPressed;\n    }\n\n    public final void setOnCancelPressed(EventHandler<RecurrencePopupEvent> value) {\n        onCancelPressedProperty().set(value);\n    }\n\n    public final EventHandler<RecurrencePopupEvent> getOnCancelPressed() {\n        return onCancelPressed == null ? null : onCancelPressedProperty().get();\n    }\n\n    public static class RecurrencePopupEvent extends Event {\n\n        public static final EventType<RecurrencePopupEvent> RECURRENCE_POPUP_CLOSED = new EventType<>(\n                Event.ANY, \"RECURRENCE_POPUP_CLOSED\");\n\n        public static final EventType<RecurrencePopupEvent> OK_PRESSED = new EventType<>(\n                RecurrencePopupEvent.RECURRENCE_POPUP_CLOSED, \"OK_PRESSED\");\n\n        public static final EventType<RecurrencePopupEvent> CANCEL_PRESSED = new EventType<>(\n                RecurrencePopupEvent.RECURRENCE_POPUP_CLOSED, \"CANCEL_PRESSED\");\n\n        public RecurrencePopupEvent(EventType<? extends Event> eventType) {\n            super(eventType);\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/ZoneIdComparator.java",
    "content": "package com.calendarfx.view.popover;\n\nimport impl.com.calendarfx.view.ZoneIdStringConverter;\n\nimport java.time.ZoneId;\nimport java.util.Comparator;\nimport java.util.Objects;\n\npublic class ZoneIdComparator implements Comparator<ZoneId> {\n\n    private ZoneIdStringConverter converter = new ZoneIdStringConverter();\n\n    public ZoneIdComparator(ZoneIdStringConverter converter) {\n        this.converter = Objects.requireNonNull(converter, \"converter can not be null\");\n    }\n\n    public ZoneIdComparator() {\n    }\n\n    @Override\n    public int compare(ZoneId zone1, ZoneId zone2) {\n        String s1 = converter.toString(zone1);\n        String s2 = converter.toString(zone2);\n        return s1.compareTo(s2);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/popover/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * A getting started set of views for creating popovers for calendar entries.\n */\npackage com.calendarfx.view.popover;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/OptionsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport impl.com.calendarfx.view.print.OptionsViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet;\n\nimport java.util.Optional;\n\n/**\n * A control that allows to toggle a couple of options inside the print preview dialog.\n * The default style class of this view is \"options-view\".\n *\n * <img src=\"doc-files/options-view.png\" alt=\"Options View\">\n */\npublic class OptionsView extends ViewTypeControl {\n\n    private static final String DEFAULT_STYLE_CLASS = \"options-view\";\n\n    /**\n     * Constructs a new view.\n     */\n    public OptionsView() {\n        getStyleClass().add(DEFAULT_STYLE_CLASS);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new OptionsViewSkin(this);\n    }\n\n    private final BooleanProperty showAllDayEntries = new SimpleBooleanProperty(this, \"showAllDayEntries\", true);\n\n    public final BooleanProperty showAllDayEntriesProperty() {\n        return showAllDayEntries;\n    }\n\n    public final boolean isShowAllDayEntries() {\n        return showAllDayEntriesProperty().get();\n    }\n\n    public final void setShowAllDayEntries(boolean showAllDayEntries) {\n        showAllDayEntriesProperty().set(showAllDayEntries);\n    }\n\n    private final BooleanProperty showEntryDetails = new SimpleBooleanProperty(this, \"showEntryDetails\", true);\n\n    public final BooleanProperty showEntryDetailsProperty() {\n        return showEntryDetails;\n    }\n\n    public final boolean isShowEntryDetails() {\n        return showEntryDetailsProperty().get();\n    }\n\n    public final void setShowEntryDetails(boolean showEntryDetails) {\n        showEntryDetailsProperty().set(showEntryDetails);\n    }\n\n    private final BooleanProperty showTimedEntries = new SimpleBooleanProperty(this, \"showTimedEntries\", true);\n\n    public final BooleanProperty showTimedEntriesProperty() {\n        return showTimedEntries;\n    }\n\n    public final boolean isShowTimedEntries() {\n        return showTimedEntriesProperty().get();\n    }\n\n    public final void setShowTimedEntries(boolean showTimedEntries) {\n        showTimedEntriesProperty().set(showTimedEntries);\n    }\n\n    private final BooleanProperty showMiniCalendars = new SimpleBooleanProperty(this, \"showMiniCalendars\", true);\n\n    public final BooleanProperty showMiniCalendarsProperty() {\n        return showMiniCalendars;\n    }\n\n    public final boolean isShowMiniCalendars() {\n        return showMiniCalendarsProperty().get();\n    }\n\n    public final void setShowMiniCalendars(boolean showMiniCalendars) {\n        showMiniCalendarsProperty().set(showMiniCalendars);\n    }\n\n    private final BooleanProperty showCalendarKeys = new SimpleBooleanProperty(this, \"showCalendarKeys\", true);\n\n    public final BooleanProperty showCalendarKeysProperty() {\n        return showCalendarKeys;\n    }\n\n    public final boolean isShowCalendarKeys() {\n        return showCalendarKeysProperty().get();\n    }\n\n    public final void setShowCalendarKeys(boolean showCalendarKeys) {\n        showCalendarKeysProperty().set(showCalendarKeys);\n    }\n\n    private final BooleanProperty showSwimlaneLayout = new SimpleBooleanProperty(this, \"showSwimlaneLayout\", true);\n\n    public final BooleanProperty showSwimlaneLayoutProperty() {\n        return showSwimlaneLayout;\n    }\n\n    public final boolean isShowSwimlaneLayout() {\n        return showSwimlaneLayoutProperty().get();\n    }\n\n    public final void setShowSwimlaneLayout(boolean showSwimlaneLayout) {\n        showSwimlaneLayoutProperty().set(showSwimlaneLayout);\n    }\n\n    private static final String PRINT_OPTIONS_CATEGORY = \"Options View\";\n\n    @Override\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowAllDayEntries((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowAllDayEntries();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showAllDayEntriesProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"All Day Entries\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"All Day Entries\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowCalendarKeys((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowCalendarKeys();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showCalendarKeysProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Calendar Keys\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show calendar keys\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowEntryDetails((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowEntryDetails();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showEntryDetailsProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Entry Details\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show entry details\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowMiniCalendars((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowMiniCalendars();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showMiniCalendarsProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Mini Calendars\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show mini calendars\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowSwimlaneLayout((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowSwimlaneLayout();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showSwimlaneLayoutProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Swimlanes Layout\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show swimlane layout\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setShowTimedEntries((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isShowTimedEntries();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(showTimedEntriesProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"Timed Entries\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Show timed entries\";\n            }\n\n            @Override\n            public String getCategory() {\n                return PRINT_OPTIONS_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/PaperView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport java.util.Objects;\nimport java.util.Set;\n\nimport impl.com.calendarfx.view.print.PaperViewSkin;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.print.Paper;\nimport javafx.print.Printer;\nimport javafx.print.PrinterAttributes;\nimport javafx.scene.control.Skin;\n\n/**\n * A control for specifying the paper size, the view type (day, week, month),\n * and the print margins. The default style class of this view is \"paper-view\".\n *\n * <img src=\"doc-files/paper-view.png\" alt=\"Paper View\">\n */\npublic class PaperView extends ViewTypeControl {\n\n    private static final String DEFAULT_STYLE = \"paper-view\";\n\n    /**\n     * Possible print margin types: default, minimum, custom.\n     */\n    public enum MarginType {\n\n        /**\n         * Used to request that the minimum margins supported by the printer\n         * shall be used for printing.\n         */\n        MINIMUM(Printer.MarginType.HARDWARE_MINIMUM),\n\n        /**\n         * Used to request that the default margins of the printer\n         * shall be used for printing.\n         */\n        DEFAULT(Printer.MarginType.DEFAULT),\n\n        /**\n         * Used to request that custom margins shall be used for printing.\n         */\n        CUSTOM(null);\n\n        Printer.MarginType type;\n\n        MarginType(Printer.MarginType type) {\n            this.type = type;\n        }\n\n        public Printer.MarginType getPrinterMarginType() {\n            return type;\n        }\n    }\n\n    /**\n     * Constructs a new paper setup control.\n     */\n    public PaperView() {\n        super();\n        getStyleClass().add(DEFAULT_STYLE);\n\n        Printer defaultPrinter = Printer.getDefaultPrinter();\n        if (defaultPrinter != null) {\n            PrinterAttributes printerAttributes = defaultPrinter.getPrinterAttributes();\n            if (printerAttributes != null) {\n                Set<Paper> supportedPapers = printerAttributes.getSupportedPapers();\n                if (supportedPapers != null) {\n                    getAvailablePapers().setAll(supportedPapers);\n                }\n            }\n        }\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new PaperViewSkin(this);\n    }\n\n    // paper support\n\n    private final ObjectProperty<Paper> paper = new SimpleObjectProperty<>(this, \"paper\", Paper.A4);\n\n    /**\n     * A property used to store the currently selected paper size.\n     *\n     * @return the paper\n     */\n    public final ObjectProperty<Paper> paperProperty() {\n        return paper;\n    }\n\n    /**\n     * Returns the value of {@link #paperProperty()}.\n     *\n     * @return the paper\n     */\n    public final Paper getPaper() {\n        return paperProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #paperProperty()}.\n     *\n     * @param paper the paper\n     */\n    public final void setPaper(Paper paper) {\n        paperProperty().set(paper);\n    }\n\n    // available papers support\n\n    private final ObservableList<Paper> availablePapers = FXCollections.observableArrayList();\n\n    /**\n     * Returns the available paper sizes.\n     *\n     * @return the paper sizes\n     */\n    public final ObservableList<Paper> getAvailablePapers() {\n        return availablePapers;\n    }\n\n    // margin type support\n\n    private final ObjectProperty<MarginType> marginType = new SimpleObjectProperty<MarginType>(this, \"marginType\", MarginType.DEFAULT) {\n        @Override\n        public void set(MarginType newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    /**\n     * A property used to store the currently requested margin type (custom, minimum, or default).\n     *\n     * @return the requested margin types\n     */\n    public final ObjectProperty<MarginType> marginTypeProperty() {\n        return marginType;\n    }\n\n    /**\n     * Returns the value of {@link #marginTypeProperty()}.\n     *\n     * @return the margin type (custom, default, minimum)\n     */\n    public final MarginType getMarginType() {\n        return marginTypeProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #marginTypeProperty()}.\n     *\n     * @param type the margin type (custom, default, minimum)\n     */\n    public final void setMarginType(MarginType type) {\n        marginTypeProperty().set(type);\n    }\n\n    // top margin support\n\n    private final DoubleProperty topMargin = new SimpleDoubleProperty(this, \"topMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(\"The margin is invalid: \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the top print margin value.\n     *\n     * @return the top margin\n     */\n    public final DoubleProperty topMarginProperty() {\n        return topMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #topMarginProperty()}.\n     *\n     * @return the top margin\n     */\n    public final double getTopMargin() {\n        return topMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #topMarginProperty()}.\n     *\n     * @param margin the top margin\n     */\n    public final void setTopMargin(double margin) {\n        topMarginProperty().set(margin);\n    }\n\n    // right margin support\n\n    private final DoubleProperty rightMargin = new SimpleDoubleProperty(this, \"rightMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(\"The margin is invalid: \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the right print margin value.\n     *\n     * @return the right margin\n     */\n    public final DoubleProperty rightMarginProperty() {\n        return rightMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #rightMarginProperty()}.\n     *\n     * @return the right margin\n     */\n    public final double getRightMargin() {\n        return rightMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #rightMarginProperty()}.\n     *\n     * @param margin the right margin\n     */\n    public final void setRightMargin(double margin) {\n        rightMarginProperty().set(margin);\n    }\n\n    // bottom margin support\n\n    private final DoubleProperty bottomMargin = new SimpleDoubleProperty(this, \"bottomMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(\"The margin is invalid: \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the bottom print margin value.\n     *\n     * @return the bottom margin\n     */\n    public final DoubleProperty bottomMarginProperty() {\n        return bottomMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #bottomMarginProperty()}.\n     *\n     * @return the bottom margin\n     */\n    public final double getBottomMargin() {\n        return bottomMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #bottomMarginProperty()}.\n     *\n     * @param margin the bottom margin\n     */\n    public final void setBottomMargin(double margin) {\n        bottomMarginProperty().set(margin);\n    }\n\n    // left margin support\n\n    private final DoubleProperty leftMargin = new SimpleDoubleProperty(this, \"leftMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(\"The margin is invalid: \" + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the left print margin value.\n     *\n     * @return the left margin\n     */\n    public final DoubleProperty leftMarginProperty() {\n        return leftMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #leftMarginProperty()}.\n     *\n     * @return the left margin\n     */\n    public final double getLeftMargin() {\n        return leftMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #leftMarginProperty()}.\n     *\n     * @param margin the left margin\n     */\n    public final void setLeftMargin(double margin) {\n        leftMarginProperty().set(margin);\n    }\n\n    private final BooleanProperty showMargin = new SimpleBooleanProperty(this,\n            \"showMargin\", true);\n\n    /**\n     * Stores the flag that indicates if margin combo will be displayed.\n     *\n     * @return showMargin\n     */\n    public BooleanProperty showMarginProperty() {\n        return showMargin;\n    }\n\n    /**\n     * Sets the value of the {@link #showMarginProperty()}.\n     *\n     * @param show boolean value\n     */\n    public void setShowMargin(boolean show) {\n        showMarginProperty().set(show);\n    }\n\n    /**\n     * Returns the value of the {@link #showMarginProperty()}.\n     *\n     * @return The flag to show/hide Margin combo.\n     */\n    public boolean isShowMargin() {\n        return showMarginProperty().get();\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/PreviewPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.view.CalendarFXControl;\nimport impl.com.calendarfx.view.print.PreviewPaneSkin;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\n\nimport java.time.LocalDate;\n\n/**\n * The preview pane wraps around the zoom pane which again wraps around the\n * printable printablePage. The preview pane features a zoom slider and\n * printablePage flipping controls that come in handy when the user requested to\n * print several pages.\n */\npublic class PreviewPane extends CalendarFXControl {\n\n    public static final String DEFAULT_STYLE = \"print-preview\";\n\n    protected PrintablePage printablePage;\n    private ZoomPane zoomPane;\n\n    /**\n     * Constructs a new preview pane.\n     */\n    public PreviewPane() {\n        PrintablePage printableP = new PrintablePage();\n        initialize(printableP);\n    }\n\n    public void initialize(PrintablePage printablePage) {\n\n        this.printablePage = printablePage;\n        zoomPane = new ZoomPane(printablePage);\n        getStyleClass().add(DEFAULT_STYLE);\n\n        final InvalidationListener layoutListener = obs -> zoomPane.requestLayout();\n        printablePage.viewTypeProperty().addListener(layoutListener);\n        printablePage.paperProperty().addListener(layoutListener);\n\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new PreviewPaneSkin(this);\n    }\n\n    public final PrintablePage getPrintablePage() {\n        return printablePage;\n    }\n\n    public final ZoomPane getZoomPane() {\n        return zoomPane;\n    }\n\n    // print start date\n\n    private final ObjectProperty<LocalDate> printStartDate = new SimpleObjectProperty<>(this, \"printStartDate\");\n\n    public final ObjectProperty<LocalDate> printStartDateProperty() {\n        return printStartDate;\n    }\n\n    public final LocalDate getPrintStartDate() {\n        return printStartDateProperty().get();\n    }\n\n    public final void setPrintStartDate(LocalDate date) {\n        printStartDateProperty().set(date);\n    }\n\n    // print end date\n\n    private final ObjectProperty<LocalDate> printEndDate = new SimpleObjectProperty<>(\n            this, \"printEndDate\");\n\n    public final ObjectProperty<LocalDate> printEndDateProperty() {\n        return printEndDate;\n    }\n\n    public final LocalDate getPrintEndDate() {\n        return printEndDateProperty().get();\n    }\n\n    public final void setPrintEndDate(LocalDate date) {\n        printEndDateProperty().set(date);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/PrintView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.SourceView;\nimport impl.com.calendarfx.view.print.PrintViewSkin;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.print.JobSettings;\nimport javafx.print.PageLayout;\nimport javafx.print.PageOrientation;\nimport javafx.print.Paper;\nimport javafx.print.PrintColor;\nimport javafx.print.Printer;\nimport javafx.print.PrinterJob;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.control.Skin;\nimport javafx.scene.image.Image;\nimport javafx.scene.transform.Scale;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.Window;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.time.temporal.WeekFields;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A print preview pane / dialog for CalendarFX. This view manages a\n * {@link PrintablePage} and binds it to the settingsView / properties that are\n * made available via the {@link SettingsView}. The default style class used by\n * this view is \"print-view\".\n *\n * <img width=\"100%\" src=\"doc-files/print-view.png\" alt=\"Print View\">\n */\npublic class PrintView extends ViewTypeControl {\n\n    private static final String DEFAULT_STYLE = \"print-view\";\n\n    private final PreviewPane previewPane = new PreviewPane();\n\n    private final SettingsView settingsView = new SettingsView();\n\n    /**\n     * Constructs a new print view.\n     */\n    public PrintView() {\n        super();\n\n        getStyleClass().add(DEFAULT_STYLE);\n\n        final PaperView paperView = settingsView.getPaperView();\n        final OptionsView optionsView = settingsView.getOptionsView();\n        final TimeRangeView timeRangeView = settingsView.getTimeRangeView();\n        final SourceView sourceView = settingsView.getSourceView();\n\n        final PrintablePage printablePage = previewPane.getPrintablePage();\n\n        paperView.viewTypeProperty().bindBidirectional(viewTypeProperty());\n        timeRangeView.weekFieldsProperty().bind(weekFieldsProperty());\n        timeRangeView.todayProperty().bind(todayProperty());\n        Util.bindBidirectional(optionsView.showSwimlaneLayoutProperty(), layoutProperty(), LAYOUT_BOOLEAN_CONVERTER);\n\n        printablePage.weekFieldsProperty().bind(weekFieldsProperty());\n        printablePage.zoneIdProperty().bind(zoneIdProperty());\n        printablePage.viewTypeProperty().bind(paperView.viewTypeProperty());\n        printablePage.paperProperty().bind(paperView.paperProperty());\n        printablePage.marginTypeProperty().bind(paperView.marginTypeProperty());\n        printablePage.bottomMarginProperty().bind(paperView.bottomMarginProperty());\n        printablePage.leftMarginProperty().bind(paperView.leftMarginProperty());\n        printablePage.rightMarginProperty().bind(paperView.rightMarginProperty());\n        printablePage.topMarginProperty().bind(paperView.topMarginProperty());\n\n        printablePage.printStartDateProperty().bind(timeRangeView.startDateProperty());\n        printablePage.printEndDateProperty().bind(timeRangeView.endDateProperty());\n        printablePage.showAllDayEntriesProperty().bind(optionsView.showAllDayEntriesProperty());\n        printablePage.showMiniCalendarsProperty().bind(optionsView.showMiniCalendarsProperty());\n        printablePage.showCalendarKeysProperty().bind(optionsView.showCalendarKeysProperty());\n        printablePage.showTimedEntriesProperty().bind(optionsView.showTimedEntriesProperty());\n        printablePage.showEntryDetailsProperty().bind(optionsView.showEntryDetailsProperty());\n        printablePage.layoutProperty().bindBidirectional(layoutProperty());\n\n        Bindings.bindContent(sourceView.getCalendarSources(), getCalendarSources());\n        Bindings.bindContent(sourceView.getCalendarVisibilityMap(), printablePage.getCalendarVisibilityMap());\n\n        Bindings.bindContent(printablePage.getCalendarSources(), getCalendarSources());\n    }\n\n    private final ObservableList<CalendarSource> calendarSources = FXCollections\n            .observableArrayList();\n\n    /**\n     * The list of all calendar sources attached to this control.\n     *\n     * @return the calendar sources\n     */\n    public final ObservableList<CalendarSource> getCalendarSources() {\n        return calendarSources;\n    }\n\n    private final ObjectProperty<LocalDate> today = new SimpleObjectProperty<>(this, \"today\", LocalDate.now());\n\n    /**\n     * Stores the date that is considered to represent \"today\". This property is\n     * initialized with {@link LocalDate#now()} but can be any date.\n     *\n     * @return the date representing \"today\"\n     */\n    public final ObjectProperty<LocalDate> todayProperty() {\n        return today;\n    }\n\n    /**\n     * Sets the value of {@link #todayProperty()}.\n     *\n     * @param date the date representing \"today\"\n     */\n    public final void setToday(LocalDate date) {\n        requireNonNull(date);\n        todayProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #todayProperty()}.\n     *\n     * @return the date representing \"today\"\n     */\n    public final LocalDate getToday() {\n        return todayProperty().get();\n    }\n\n    private final ObjectProperty<LocalDate> date = new SimpleObjectProperty<>(this, \"date\", LocalDate.now());\n\n    /**\n     * Stores the Calendar selected date. It's needed for some process in TimeRangeView.\n     * Initialized with {@link LocalDate#now()} but It wil be binded to {@link CalendarView #dateProperty()}.\n     *\n     * @return The Calendar date\n     */\n    public final ObjectProperty<LocalDate> dateProperty() {\n        return date;\n    }\n\n    /**\n     * Returns the value of {@link #dateProperty()}.\n     *\n     * @return the date representing \"Calendar date\"\n     */\n    public final LocalDate getDate() {\n        return dateProperty().get();\n    }\n\n    private final ObjectProperty<DateControl.Layout> layout = new SimpleObjectProperty<>(this, \"layout\", DateControl.Layout.STANDARD);\n\n    /**\n     * Stores the strategy used by the view to layout the entries of several\n     * calendars at once. The standard layout ignores the source calendar of an\n     * entry and finds the next available place in the UI that satisfies the\n     * time bounds of the entry. The {@link DateControl.Layout#SWIMLANE}\n     * strategy allocates a separate column for each calendar and resolves\n     * overlapping entry conflicts within that column. Swim lanes are especially\n     * useful for resource booking systems (rooms, people, trucks).\n     *\n     * @return the layout strategy of the view\n     */\n    public final ObjectProperty<DateControl.Layout> layoutProperty() {\n        return layout;\n    }\n\n    /**\n     * Sets the value of {@link #layoutProperty()}.\n     *\n     * @param layout the layout\n     */\n    public final void setLayout(DateControl.Layout layout) {\n        requireNonNull(layout);\n        layoutProperty().set(layout);\n    }\n\n    /**\n     * Returns the value of {@link #layoutProperty()}.\n     *\n     * @return the layout strategy\n     */\n    public final DateControl.Layout getLayout() {\n        return layoutProperty().get();\n    }\n\n    private final ObjectProperty<WeekFields> weekFields = new SimpleObjectProperty<>(this, \"weekFields\", WeekFields.ISO);\n\n    /**\n     * Week fields are used to determine the first day of a week (e.g. \"Monday\"\n     * in Germany or \"Sunday\" in the US). It is also used to calculate the week\n     * number as the week fields determine how many days are needed in the first\n     * week of a year. This property is initialized with {@link WeekFields#ISO}.\n     *\n     * @return the week fields\n     */\n    public final ObjectProperty<WeekFields> weekFieldsProperty() {\n        return weekFields;\n    }\n\n    /**\n     * Sets the value of {@link #weekFieldsProperty()}.\n     *\n     * @param weekFields the new week fields\n     */\n    public final void setWeekFields(WeekFields weekFields) {\n        requireNonNull(weekFields);\n        weekFieldsProperty().set(weekFields);\n    }\n\n    /**\n     * Returns the value of {@link #weekFieldsProperty()}.\n     *\n     * @return the week fields\n     */\n    public final WeekFields getWeekFields() {\n        return weekFieldsProperty().get();\n    }\n\n    /**\n     * A convenience method to lookup the first day of the week (\"Monday\" in\n     * Germany, \"Sunday\" in the US). This method delegates to\n     * {@link WeekFields#getFirstDayOfWeek()}.\n     *\n     * @return the first day of the week\n     * @see #weekFieldsProperty()\n     */\n    public final DayOfWeek getFirstDayOfWeek() {\n        return getWeekFields().getFirstDayOfWeek();\n    }\n\n    public final void loadDropDownValues(LocalDate date) {\n        settingsView.getTimeRangeView().loadDropDownValues(date);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new PrintViewSkin(this);\n    }\n\n    /**\n     * Returns the preview pane sub control.\n     *\n     * @return the preview pane control\n     */\n    public final PreviewPane getPreviewPane() {\n        return previewPane;\n    }\n\n    /**\n     * Returns the settings view sub control.\n     *\n     * @return the settings view\n     */\n    public final SettingsView getSettingsView() {\n        return settingsView;\n    }\n\n    private final ObjectProperty<EventHandler<ActionEvent>> onContinue = new SimpleObjectProperty<>(this, \"onContinue\", evt -> doPrint());\n\n    /**\n     * Stores an event handler that will be invoked when the user clicks on the\n     * \"continue\" button. The default event handler invokes the\n     * {@link #doPrint()} method.\n     *\n     * @return the event handler used by the \"continue\" button\n     */\n    public final ObjectProperty<EventHandler<ActionEvent>> onContinueProperty() {\n        return onContinue;\n    }\n\n    /**\n     * Returns the value of the {@link #onContinueProperty()}.\n     *\n     * @return the event handler invoked by the \"continue\" button.\n     */\n    public final EventHandler<ActionEvent> getOnContinue() {\n        return onContinueProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #onContinueProperty()}.\n     *\n     * @param handler the event handler invoked by the \"continue\" button.\n     */\n    public final void setOnContinue(EventHandler<ActionEvent> handler) {\n        onContinueProperty().set(handler);\n    }\n\n    private final ObjectProperty<EventHandler<ActionEvent>> onCancel = new SimpleObjectProperty<>(this, \"onCancel\", evt -> hide());\n\n    /**\n     * Stores an event handler that will be invoked when the user clicks on the\n     * \"cancel\" button. The default event handler invokes the {@link #hide()}\n     * method.\n     *\n     * @return the event handler used by the \"cancel\" button\n     */\n    public final ObjectProperty<EventHandler<ActionEvent>> onCancelProperty() {\n        return onCancel;\n    }\n\n    /**\n     * Returns the value of the {@link #onCancelProperty()}.\n     *\n     * @return the event handler invoked by the \"continue\" button.\n     */\n    public final EventHandler<ActionEvent> getOnCancel() {\n        return onCancelProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #onCancelProperty()}.\n     *\n     * @param handler the event handler invoked by the \"cancel\" button.\n     */\n    public final void setOnCancel(EventHandler<ActionEvent> handler) {\n        onCancelProperty().set(handler);\n    }\n\n    private Stage dialog;\n\n    private final ObjectProperty<Image> printIcon = new SimpleObjectProperty<Image>(this, \"printIcon\", null);\n\n    /**\n     * Stores the image of Print dialog. This property is null by default, but\n     * if custom icon is required it will be saved here .\n     *\n     * @return the date representing \"today\"\n     */\n    public final ObjectProperty<Image> printIconProperty() {\n        return printIcon;\n    }\n\n    /**\n     * Sets the value of the {@link #printIconProperty()}.\n     *\n     * @param image will be the icon of window/dialog.\n     */\n    public final void setPrintIcon(Image image) {\n        requireNonNull(image);\n        printIconProperty().set(image);\n    }\n\n    /**\n     * Returns the value of {@link #printIconProperty()}.\n     *\n     * @return the icon of Print dialog if is set, null otherwise.\n     */\n    public final Image getPrintIcon() {\n        return printIconProperty().get();\n    }\n\n    /**\n     * Creates an application-modal dialog and shows it after adding the print\n     * view to it.\n     *\n     * @param owner the owner window of the dialog\n     */\n    public void show(Window owner) {\n        InvalidationListener viewTypeListener = obs -> loadDropDownValues(getDate());\n\n        if (dialog != null) {\n            dialog.show();\n        } else {\n            Scene ownerScene = owner.getScene();\n\n            TimeRangeView timeRange = getSettingsView().getTimeRangeView();\n            Scene scene = new Scene(this);\n            scene.getStylesheets().addAll(ownerScene.getStylesheets());\n\n            dialog = new Stage();\n            dialog.initOwner(owner);\n            dialog.setScene(scene);\n            dialog.sizeToScene();\n            dialog.centerOnScreen();\n            dialog.setTitle(Messages.getString(\"PrintView.TITLE_LABEL\"));\n            dialog.initModality(Modality.APPLICATION_MODAL);\n\n            if (getPrintIcon() != null) {\n                dialog.getIcons().add(getPrintIcon());\n            }\n\n            dialog.setOnHidden(obs -> {\n                timeRange.cleanOldValues();\n                timeRange.viewTypeProperty().removeListener(viewTypeListener);\n            });\n\n            dialog.setOnShown(obs -> timeRange.viewTypeProperty().addListener(viewTypeListener));\n\n            dialog.show();\n        }\n    }\n\n    /**\n     * Hides the dialog.\n     */\n    public final void hide() {\n        if (dialog != null) {\n            dialog.hide();\n        }\n    }\n\n    /**\n     * Performs the actual printing of the calendars.\n     */\n    protected void doPrint() {\n\n        PrintablePage pageInView = previewPane.getPrintablePage();\n        PrintablePage pageToPrint = new PrintablePage();\n\n        try {\n            pageInView.bindPage(pageToPrint);\n\n            Printer printer = Printer.getDefaultPrinter();\n\n            if (printer == null || settingsView.getPaperView()\n                    .getAvailablePapers().isEmpty()) {\n                // Show an Error\n                Alert alert = new Alert(AlertType.INFORMATION);\n                alert.initOwner(dialog);\n                alert.setTitle(Messages.getString(\"DateControl.TITLE_CALENDAR_PROBLEM\"));\n                alert.setHeaderText(Messages.getString(\"PrintView.NO_PRINTERS\"));\n                alert.setContentText(Messages.getString(\"PrintView.ERROR_NO_PRINTER\"));\n                alert.show();\n                return;\n            }\n\n            hide();\n            LoggingDomain.PRINTING.fine(\"printer = \" + printer);\n\n            PageLayout layout = null;\n\n            final Paper paper = pageInView.getPaper();\n            final PageOrientation pageOrientation = pageInView.getViewType().getPageOrientation();\n            final PaperView.MarginType marginType = pageInView.getMarginType();\n\n            LoggingDomain.PRINTING.fine(\"paper = \" + paper);\n            LoggingDomain.PRINTING.fine(\"pageOrientation = \" + pageOrientation);\n            LoggingDomain.PRINTING.fine(\"marginType = \" + marginType);\n            LoggingDomain.PRINTING.fine(\"custom margins = left: \" + pageInView.getLeftMargin()\n                    + \", right: \" + pageInView.getRightMargin()\n                    + \", top: \" + pageInView.getTopMargin()\n                    + \", bottom: \" + pageInView.getBottomMargin());\n\n            switch (marginType) {\n                case DEFAULT:\n                    layout = printer.createPageLayout(paper, pageOrientation, Printer.MarginType.DEFAULT);\n                    break;\n                case MINIMUM:\n                    layout = printer.createPageLayout(paper, pageOrientation, Printer.MarginType.HARDWARE_MINIMUM);\n                    break;\n                case CUSTOM:\n                    layout = printer.createPageLayout(paper, pageOrientation,\n                            pageInView.getLeftMargin(), pageInView.getRightMargin(),\n                            pageInView.getTopMargin(),\n                            pageInView.getBottomMargin());\n                    break;\n                default:\n                    throw new IllegalArgumentException(\"unknown margin type \" + marginType);\n            }\n\n            // sizes of print page and physical page\n            double pageWidth = pageToPrint.prefWidth(-1);\n            double pageHeight = pageToPrint.prefHeight(-1);\n\n            double printableWidth = layout.getPrintableWidth();\n            double printableHeight = layout.getPrintableHeight();\n\n            // scaling\n            double scaleX = printableWidth / pageWidth;\n            double scaleY = printableHeight / pageHeight;\n            double scale = Math.min(scaleX, scaleY);\n\n            LoggingDomain.PRINTING.fine(\"pageWidth / pageHeight = \" + pageWidth + \" / \" + pageHeight);\n            LoggingDomain.PRINTING.fine(\"printableWidth / printableHeight = \" + printableWidth + \" / \" + printableHeight);\n            LoggingDomain.PRINTING.fine(\"scaleX / scaleY = \" + scaleX + \" / \" + scaleY);\n            LoggingDomain.PRINTING.fine(\"scale = \" + scale);\n\n            pageToPrint.applyCss();\n            pageToPrint.layout();\n            pageToPrint.getTransforms().add(new Scale(scale, scale));\n\n            // transformation = center\n            final double translateX = (printableWidth - (pageWidth * scale)) / 2;\n            final double translateY = (printableHeight - (pageHeight * scale)) / 2;\n\n            LoggingDomain.PRINTING.fine(\"translateX / translateY = \" + translateX + \" / \" + translateY);\n\n            pageToPrint.setTranslateX(translateX);\n            pageToPrint.setTranslateY(translateY);\n\n            PrinterJob job = PrinterJob.createPrinterJob();\n            JobSettings settings = job.getJobSettings();\n            settings.setJobName(Messages.getString(\"PrintView.TITLE_LABEL\"));\n            settings.setPageLayout(layout);\n            settings.setPrintColor(PrintColor.COLOR);\n            boolean proceed = job.showPrintDialog(dialog.getOwner().getScene().getWindow());\n\n            if (proceed) {\n                do {\n                    boolean success = job.printPage(pageToPrint);\n                    if (!success) {\n                        break;\n                    }\n                } while (pageToPrint.next());\n\n                job.endJob();\n            }\n        } finally {\n            pageInView.unbindPage(pageToPrint);\n        }\n    }\n\n    private static final Util.Converter<Boolean, DateControl.Layout> LAYOUT_BOOLEAN_CONVERTER = new Util.Converter<>() {\n\n        @Override\n        public Boolean toLeft(DateControl.Layout right) {\n            return right == DateControl.Layout.SWIMLANE;\n        }\n\n        @Override\n        public DateControl.Layout toRight(Boolean left) {\n            return Boolean.TRUE.equals(left) ? DateControl.Layout.SWIMLANE : DateControl.Layout.STANDARD;\n        }\n    };\n\n    private final ObjectProperty<ZoneId> zoneId = new SimpleObjectProperty<>(this, \"zoneId\", ZoneId.systemDefault());\n\n    public ZoneId getZoneId() {\n        return zoneId.get();\n    }\n\n    /**\n     * The time zone to use for the printing operation.\n     *\n     * @return the time zone\n     */\n    public ObjectProperty<ZoneId> zoneIdProperty() {\n        return zoneId;\n    }\n\n    public void setZoneId(ZoneId zoneId) {\n        this.zoneId.set(zoneId);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/PrintablePage.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.DetailedDayView;\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.MonthView;\nimport com.calendarfx.view.print.PaperView.MarginType;\nimport impl.com.calendarfx.view.print.PrintablePageSkin;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyIntegerProperty;\nimport javafx.beans.property.ReadOnlyIntegerWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.event.Event;\nimport javafx.event.WeakEventHandler;\nimport javafx.print.PageOrientation;\nimport javafx.print.Paper;\nimport javafx.scene.control.Skin;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.input.MouseEvent;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * A control that is designed and laid out in such a way that it can be nicely\n * printed. The control is capable of switching between a day view, a week view,\n * or a month view.\n */\npublic class PrintablePage extends DateControl {\n\n    public static final String DEFAULT_STYLE = \"print-page\";\n    public static final String INVALID_MARGIN = \"The margin is invalid: \";\n\n    private final PrintPeriodSplitter periodSplitter;\n\n    private final DetailedDayView detailedDayView;\n    private final DetailedWeekView detailedWeekView;\n    private final MonthView monthView;\n    private final ObjectProperty<Map<ViewType, DateTimeFormatter>> formatterMapProperty = new SimpleObjectProperty<>(this, \"formatterMapProperty\");\n\n    private final WeakEventHandler<MouseEvent> weakMouseHandler = new WeakEventHandler<>(Event::consume);\n\n    public PrintablePage() {\n        getStyleClass().add(DEFAULT_STYLE);\n\n        addEventFilter(KeyEvent.ANY, Event::consume);\n\n        setFocusTraversable(false);\n\n        // day view\n        detailedDayView = createDetailedDayView();\n        Bindings.bindContent(detailedDayView.getCalendarSources(), getCalendarSources());\n        Bindings.bindContent(detailedDayView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n\n        // week view\n        detailedWeekView = createDetailedWeekView();\n        Bindings.bindContent(detailedWeekView.getCalendarSources(), getCalendarSources());\n        Bindings.bindContent(detailedWeekView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n\n        // month view\n        monthView = createMonthView();\n        Bindings.bindContent(monthView.getCalendarSources(), getCalendarSources());\n        Bindings.bindContentBidirectional(monthView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n\n        updateView();\n        updateDimension();\n\n        paperProperty().addListener(obs -> updateDimension());\n        viewTypeProperty().addListener(obs -> updateView());\n\n        periodSplitter = new PrintPeriodSplitter(this);\n        formatterMapProperty.set(new HashMap<>());\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new PrintablePageSkin(this);\n    }\n\n    public final DetailedDayView getDayView() {\n        return detailedDayView;\n    }\n\n    public final DetailedWeekView getWeekView() {\n        return detailedWeekView;\n    }\n\n    public final MonthView getMonthView() {\n        return monthView;\n    }\n\n    // view type support\n\n    private final ObjectProperty<ViewType> viewType = new SimpleObjectProperty<>(this, \"viewType\", ViewType.DAY_VIEW) {\n        @Override\n        public void set(ViewType newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ObjectProperty<ViewType> viewTypeProperty() {\n        return viewType;\n    }\n\n    public final ViewType getViewType() {\n        return viewTypeProperty().get();\n    }\n\n    public final void setViewType(ViewType viewType) {\n        viewTypeProperty().set(viewType);\n    }\n\n    // current view support (read-only)\n\n    private final ReadOnlyObjectWrapper<DateControl> view = new ReadOnlyObjectWrapper<>(this, \"view\") {\n        @Override\n        public void set(DateControl newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ReadOnlyObjectProperty<DateControl> viewProperty() {\n        return view.getReadOnlyProperty();\n    }\n\n    public final DateControl getView() {\n        return view.get();\n    }\n\n    // private because read-only property\n    private void setView(DateControl view) {\n        this.view.set(view);\n    }\n\n    // margin type support\n\n    private final ObjectProperty<MarginType> marginType = new SimpleObjectProperty<>(this, \"marginType\", MarginType.DEFAULT) {\n        @Override\n        public void set(MarginType newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    /**\n     * A property used to store the currently requested margin type (custom,\n     * minimum, or default).\n     *\n     * @return the requested margin types\n     */\n    public final ObjectProperty<MarginType> marginTypeProperty() {\n        return marginType;\n    }\n\n    /**\n     * Returns the value of {@link #marginTypeProperty()}.\n     *\n     * @return the margin type (custom, default, minimum)\n     */\n    public final MarginType getMarginType() {\n        return marginTypeProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #marginTypeProperty()}.\n     *\n     * @param type\n     *            the margin type (custom, default, minimum)\n     */\n    public final void setMarginType(MarginType type) {\n        marginTypeProperty().set(type);\n    }\n\n    /**\n     * Object property which contains a Map with the custom date time\n     * formatters. Depending on the {@link ViewType} a specific formatter is\n     * returned.\n     * \n     * @return the ObjectProperty\n     */\n    public ObjectProperty<Map<ViewType, DateTimeFormatter>> formatterMapProperty() {\n        return formatterMapProperty;\n    }\n\n    /**\n     * Gets the Formatter Map from the property.\n     * \n     * @return the Formatter Map.\n     */\n    public Map<ViewType, DateTimeFormatter> getFormatterMap() {\n        return formatterMapProperty.get();\n    }\n\n    /**\n     * Sets the DateTimeFormatter on the Day Label located in the day page.\n     * Notice that this is also affecting the page that is going to be printed.\n     * \n     * @param formatter\n     *            the DateTimeFormatter\n     */\n    public void setDayDateTimeFormatter(DateTimeFormatter formatter) {\n        if (getFormatterMap().get(ViewType.DAY_VIEW) == null) {\n            getFormatterMap().put(ViewType.DAY_VIEW, formatter);\n        } else {\n            getFormatterMap().replace(ViewType.DAY_VIEW, formatter);\n        }\n    }\n\n    /**\n     * Sets the DateTimeFormatter on the Week Label located in the week page.\n     * Notice that this is also affecting the page that is going to be printed.\n     * \n     * @param formatter\n     *            the DateTimeFormatter\n     */\n    public void setWeekDateTimeFormatter(DateTimeFormatter formatter) {\n        if (getFormatterMap().get(ViewType.WEEK_VIEW) == null) {\n            getFormatterMap().put(ViewType.WEEK_VIEW, formatter);\n        } else {\n            getFormatterMap().replace(ViewType.WEEK_VIEW, formatter);\n        }\n    }\n\n    /**\n     * Sets the DateTimeFormatter on the Month Label located in the month page.\n     * Notice that this is also affecting the page that is going to be printed.\n     * \n     * @param formatter\n     *            the DateTimeFormatter\n     */\n    public void setMonthDateTimeFormatter(DateTimeFormatter formatter) {\n        if (getFormatterMap().get(ViewType.MONTH_VIEW) == null) {\n            getFormatterMap().put(ViewType.MONTH_VIEW, formatter);\n        } else {\n            getFormatterMap().replace(ViewType.MONTH_VIEW, formatter);\n        }\n    }\n\n    // top margin support\n\n    private final DoubleProperty topMargin = new SimpleDoubleProperty(this, \"topMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(INVALID_MARGIN + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the top print margin value.\n     *\n     * @return the top margin\n     */\n    public final DoubleProperty topMarginProperty() {\n        return topMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #topMarginProperty()}.\n     *\n     * @return the top margin\n     */\n    public final double getTopMargin() {\n        return topMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #topMarginProperty()}.\n     *\n     * @param margin\n     *            the top margin\n     */\n    public final void setTopMargin(double margin) {\n        topMarginProperty().set(margin);\n    }\n\n    // right margin support\n\n    private final DoubleProperty rightMargin = new SimpleDoubleProperty(this, \"rightMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(INVALID_MARGIN + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the right print margin value.\n     *\n     * @return the right margin\n     */\n    public final DoubleProperty rightMarginProperty() {\n        return rightMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #rightMarginProperty()}.\n     *\n     * @return the right margin\n     */\n    public final double getRightMargin() {\n        return rightMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #rightMarginProperty()}.\n     *\n     * @param margin\n     *            the right margin\n     */\n    public final void setRightMargin(double margin) {\n        rightMarginProperty().set(margin);\n    }\n\n    // bottom margin support\n\n    private final DoubleProperty bottomMargin = new SimpleDoubleProperty(this, \"bottomMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(INVALID_MARGIN + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the bottom print margin value.\n     *\n     * @return the bottom margin\n     */\n    public final DoubleProperty bottomMarginProperty() {\n        return bottomMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #bottomMarginProperty()}.\n     *\n     * @return the bottom margin\n     */\n    public final double getBottomMargin() {\n        return bottomMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #bottomMarginProperty()}.\n     *\n     * @param margin\n     *            the bottom margin\n     */\n    public final void setBottomMargin(double margin) {\n        bottomMarginProperty().set(margin);\n    }\n\n    // left margin support\n\n    private final DoubleProperty leftMargin = new SimpleDoubleProperty(this, \"leftMargin\") {\n        @Override\n        public void set(double newValue) {\n            if (newValue < 0) {\n                throw new IllegalArgumentException(INVALID_MARGIN + newValue);\n            }\n            super.set(newValue);\n        }\n    };\n\n    /**\n     * Stores the left print margin value.\n     *\n     * @return the left margin\n     */\n    public final DoubleProperty leftMarginProperty() {\n        return leftMargin;\n    }\n\n    /**\n     * Returns the value of the {@link #leftMarginProperty()}.\n     *\n     * @return the left margin\n     */\n    public final double getLeftMargin() {\n        return leftMarginProperty().get();\n    }\n\n    /**\n     * Sets the value of the {@link #leftMarginProperty()}.\n     *\n     * @param margin\n     *            the left margin\n     */\n    public final void setLeftMargin(double margin) {\n        leftMarginProperty().set(margin);\n    }\n\n    // print start date support\n\n    private final ObjectProperty<LocalDate> printStartDate = new SimpleObjectProperty<>(this, \"printStartDate\", getToday()) {\n        @Override\n        public void set(LocalDate newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ObjectProperty<LocalDate> printStartDateProperty() {\n        return printStartDate;\n    }\n\n    public final LocalDate getPrintStartDate() {\n        return printStartDateProperty().get();\n    }\n\n    public final void setPrintStartDate(LocalDate date) {\n        printStartDateProperty().set(date);\n    }\n\n    // print end date support\n    private final ObjectProperty<LocalDate> printEndDate = new SimpleObjectProperty<>(this, \"printEndDate\", getToday()) {\n        @Override\n        public void set(LocalDate newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ObjectProperty<LocalDate> printEndDateProperty() {\n        return printEndDate;\n    }\n\n    public final LocalDate getPrintEndDate() {\n        return printEndDateProperty().get();\n    }\n\n    public final void setPrintEndDate(LocalDate date) {\n        printEndDateProperty().set(date);\n    }\n\n    // page start date support\n\n    private final ReadOnlyObjectWrapper<LocalDate> pageStartDate = new ReadOnlyObjectWrapper<>(this, \"pageStartDate\", getToday());\n\n    public final ReadOnlyObjectProperty<LocalDate> pageStartDateProperty() {\n        return pageStartDate.getReadOnlyProperty();\n    }\n\n    public final LocalDate getPageStartDate() {\n        return pageStartDate.get();\n    }\n\n    public void setPageStartDate(LocalDate date) {\n        this.pageStartDate.set(date);\n    }\n\n    // page end date support\n\n    private final ReadOnlyObjectWrapper<LocalDate> pageEndDate = new ReadOnlyObjectWrapper<>(this, \"pageEndDate\", getToday());\n\n    public final ReadOnlyObjectProperty<LocalDate> pageEndDateProperty() {\n        return pageEndDate.getReadOnlyProperty();\n    }\n\n    public final LocalDate getPageEndDate() {\n        return pageEndDate.get();\n    }\n\n    private void setPageEndDate(LocalDate date) {\n        this.pageEndDate.set(date);\n    }\n\n    // paper support\n\n    private final ObjectProperty<Paper> paper = new SimpleObjectProperty<>(this, \"paper\", Paper.A4) {\n        @Override\n        public void set(Paper newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    public final ObjectProperty<Paper> paperProperty() {\n        return paper;\n    }\n\n    public final Paper getPaper() {\n        return paperProperty().get();\n    }\n\n    public final void setPaper(Paper paper) {\n        paperProperty().set(paper);\n    }\n\n    // show all day entries support\n\n    private final BooleanProperty showAllDayEntries = new SimpleBooleanProperty(this, \"showAllDayEntries\", true);\n\n    public final BooleanProperty showAllDayEntriesProperty() {\n        return showAllDayEntries;\n    }\n\n    public final boolean isShowAllDayEntries() {\n        return showAllDayEntriesProperty().get();\n    }\n\n    public final void setShowAllDayEntries(boolean show) {\n        showAllDayEntriesProperty().set(show);\n    }\n\n    // show mini calendars support\n\n    private final BooleanProperty showMiniCalendars = new SimpleBooleanProperty(this, \"showMiniCalendars\", true);\n\n    public final BooleanProperty showMiniCalendarsProperty() {\n        return showMiniCalendars;\n    }\n\n    public final boolean isShowMiniCalendars() {\n        return showMiniCalendarsProperty().get();\n    }\n\n    public final void setShowMiniCalendars(boolean show) {\n        showMiniCalendarsProperty().set(show);\n    }\n\n    // show calendar keys support\n\n    private final BooleanProperty showCalendarKeys = new SimpleBooleanProperty(this, \"showCalendarKeys\", true);\n\n    public final BooleanProperty showCalendarKeysProperty() {\n        return showCalendarKeys;\n    }\n\n    public final boolean isShowCalendarKeys() {\n        return showCalendarKeysProperty().get();\n    }\n\n    public final void setShowCalendarKeys(boolean show) {\n        showCalendarKeysProperty().set(show);\n    }\n\n    private final BooleanProperty showTimedEntries = new SimpleBooleanProperty(this, \"showTimedEntries\", true);\n\n    public final BooleanProperty showTimedEntriesProperty() {\n        return showTimedEntries;\n    }\n\n    public final boolean isShowTimedEntries() {\n        return showTimedEntriesProperty().get();\n    }\n\n    public final void setShowTimedEntries(boolean show) {\n        showTimedEntriesProperty().set(show);\n    }\n\n    private final BooleanProperty showEntryDetails = new SimpleBooleanProperty(this, \"showEntryDetails\", true);\n\n    public final BooleanProperty showEntryDetailsProperty() {\n        return showEntryDetails;\n    }\n\n    public final boolean isShowEntryDetails() {\n        return showEntryDetailsProperty().get();\n    }\n\n    public final void setShowEntryDetails(boolean show) {\n        showEntryDetailsProperty().set(show);\n    }\n\n    private final ReadOnlyIntegerWrapper pageNumber = new ReadOnlyIntegerWrapper(this, \"pageNumber\");\n\n    public final ReadOnlyIntegerProperty pageNumberProperty() {\n        return pageNumber.getReadOnlyProperty();\n    }\n\n    public final int getPageNumber() {\n        return pageNumber.get();\n    }\n\n    private void setPageNumber(int number) {\n        this.pageNumber.set(number);\n    }\n\n    private final ReadOnlyIntegerWrapper totalPages = new ReadOnlyIntegerWrapper(this, \"totalPages\");\n\n    public final ReadOnlyIntegerProperty totalPagesProperty() {\n        return totalPages.getReadOnlyProperty();\n    }\n\n    public final int getTotalPages() {\n        return totalPages.get();\n    }\n\n    private void setTotalPages(int total) {\n        this.totalPages.set(total);\n    }\n\n    public final boolean next() {\n        return periodSplitter.next();\n    }\n\n    public final boolean back() {\n        return periodSplitter.back();\n    }\n\n    public final void bindPage(PrintablePage otherPage) {\n        super.bind(otherPage, true);\n\n        Bindings.bindBidirectional(otherPage.viewTypeProperty(), viewTypeProperty());\n        Bindings.bindBidirectional(otherPage.paperProperty(), paperProperty());\n        Bindings.bindBidirectional(otherPage.showAllDayEntriesProperty(), showAllDayEntriesProperty());\n        Bindings.bindBidirectional(otherPage.showCalendarKeysProperty(), showCalendarKeysProperty());\n        Bindings.bindBidirectional(otherPage.showMiniCalendarsProperty(), showMiniCalendarsProperty());\n        Bindings.bindBidirectional(otherPage.showTimedEntriesProperty(), showTimedEntriesProperty());\n        Bindings.bindBidirectional(otherPage.printStartDateProperty(), printStartDateProperty());\n        Bindings.bindBidirectional(otherPage.printEndDateProperty(), printEndDateProperty());\n        Bindings.bindBidirectional(otherPage.formatterMapProperty(), formatterMapProperty());\n    }\n\n    public final void unbindPage(PrintablePage otherPage) {\n        super.unbind(otherPage);\n\n        Bindings.unbindBidirectional(otherPage.viewTypeProperty(), viewTypeProperty());\n        Bindings.unbindBidirectional(otherPage.paperProperty(), paperProperty());\n        Bindings.unbindBidirectional(otherPage.showAllDayEntriesProperty(), showAllDayEntriesProperty());\n        Bindings.unbindBidirectional(otherPage.showCalendarKeysProperty(), showCalendarKeysProperty());\n        Bindings.unbindBidirectional(otherPage.showMiniCalendarsProperty(), showMiniCalendarsProperty());\n        Bindings.unbindBidirectional(otherPage.showTimedEntriesProperty(), showTimedEntriesProperty());\n        Bindings.unbindBidirectional(otherPage.printStartDateProperty(), printStartDateProperty());\n        Bindings.unbindBidirectional(otherPage.printEndDateProperty(), printEndDateProperty());\n        Bindings.unbindBidirectional(otherPage.formatterMapProperty(), formatterMapProperty());\n    }\n\n    private void updateView() {\n        DateControl newView;\n        removeDataBindings();\n\n        switch (getViewType()) {\n        case DAY_VIEW:\n            newView = detailedDayView;\n            break;\n        case WEEK_VIEW:\n            newView = detailedWeekView;\n            break;\n        case MONTH_VIEW:\n            newView = monthView;\n            break;\n        default:\n            throw new UnsupportedOperationException(\"unsupported view type: \" + getViewType());\n        }\n\n        Bindings.bindContent(newView.getCalendarSources(), getCalendarSources());\n        Bindings.bindContentBidirectional(newView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        setView(newView);\n        updateDimension();\n    }\n\n    /**\n     * Removes all bindings related with the calendar sources and visibility\n     * map.\n     */\n    private void removeDataBindings() {\n        Bindings.unbindContent(detailedDayView.getCalendarSources(), getCalendarSources());\n        Bindings.unbindContentBidirectional(detailedDayView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        Bindings.unbindContent(detailedWeekView.getCalendarSources(), getCalendarSources());\n        Bindings.unbindContentBidirectional(detailedWeekView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n        Bindings.unbindContent(monthView.getCalendarSources(), getCalendarSources());\n        Bindings.unbindContentBidirectional(monthView.getCalendarVisibilityMap(), getCalendarVisibilityMap());\n    }\n\n    private void updateDimension() {\n        final double SIZE_MULTIPLIER = 1.5;\n        if (getViewType().getPageOrientation() == PageOrientation.PORTRAIT) {\n            setPrefHeight(getPaper().getHeight() * SIZE_MULTIPLIER);\n            setPrefWidth(getPaper().getWidth() * SIZE_MULTIPLIER);\n        } else {\n            setPrefHeight(getPaper().getWidth() * SIZE_MULTIPLIER);\n            setPrefWidth(getPaper().getHeight() * SIZE_MULTIPLIER);\n        }\n    }\n\n    /**\n     * Default configuration for detailed day view in the preview pane.\n     * \n     * @return the detailed day view\n     */\n    private DetailedDayView createDetailedDayView() {\n        DetailedDayView newDetailedDayView = new DetailedDayView();\n        newDetailedDayView.setShowScrollBar(false);\n        newDetailedDayView.setShowToday(false);\n        newDetailedDayView.setEnableCurrentTimeCircle(false);\n        newDetailedDayView.setEnableCurrentTimeMarker(false);\n        newDetailedDayView.weekFieldsProperty().bind(weekFieldsProperty());\n        newDetailedDayView.showAllDayViewProperty().bind(showAllDayEntriesProperty());\n        newDetailedDayView.showAgendaViewProperty().bind(showEntryDetailsProperty());\n        newDetailedDayView.layoutProperty().bind(layoutProperty());\n        newDetailedDayView.dateProperty().bind(pageStartDateProperty());\n        newDetailedDayView.addEventFilter(MouseEvent.ANY, weakMouseHandler);\n        newDetailedDayView.zoneIdProperty().bind(zoneIdProperty());\n        configureDetailedDayView(newDetailedDayView, true);\n        return newDetailedDayView;\n    }\n\n    /**\n     * The idea of this method is to be able to change the default configuration\n     * of the detailed day view in the preview pane. Especially being able to show\n     * all the hours in the print view.\n     * \n     * @param newDetailedDayView\n     *            view.\n     * @param trimTimeBounds\n     *            define if trim or not the hours in the day view\n     */\n    protected void configureDetailedDayView(DetailedDayView newDetailedDayView, boolean trimTimeBounds) {\n        newDetailedDayView.getDayView().setStartTime(LocalTime.MIN);\n        newDetailedDayView.getDayView().setEndTime(LocalTime.MAX);\n        newDetailedDayView.getDayView().setEarlyLateHoursStrategy(DayViewBase.EarlyLateHoursStrategy.HIDE);\n        newDetailedDayView.getDayView().setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_COUNT);\n        newDetailedDayView.getDayView().setVisibleHours(24);\n        newDetailedDayView.getDayView().setTrimTimeBounds(trimTimeBounds);\n    }\n\n    /**\n     * Default configuration for Detailed Week view in the preview Pane.\n     * \n     * @return the detailed week view\n     */\n    private DetailedWeekView createDetailedWeekView() {\n        DetailedWeekView newDetailedWeekView = new DetailedWeekView();\n        newDetailedWeekView.setShowScrollBar(false);\n        newDetailedWeekView.layoutProperty().bind(layoutProperty());\n        newDetailedWeekView.setEnableCurrentTimeCircle(false);\n        newDetailedWeekView.setEnableCurrentTimeMarker(false);\n        newDetailedWeekView.showAllDayViewProperty().bind(showAllDayEntriesProperty());\n        newDetailedWeekView.weekFieldsProperty().bind(weekFieldsProperty());\n        newDetailedWeekView.setStartTime(LocalTime.MIN);\n        newDetailedWeekView.setEndTime(LocalTime.MAX);\n        newDetailedWeekView.setEarlyLateHoursStrategy(DayViewBase.EarlyLateHoursStrategy.HIDE);\n        newDetailedWeekView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_COUNT);\n        newDetailedWeekView.setVisibleHours(24);\n        newDetailedWeekView.addEventFilter(MouseEvent.ANY, weakMouseHandler);\n        newDetailedWeekView.dateProperty().bind(pageStartDateProperty());\n        newDetailedWeekView.zoneIdProperty().bind(zoneIdProperty());\n        configureDetailedWeekView(newDetailedWeekView, true);\n        return newDetailedWeekView;\n    }\n\n    /**\n     * The idea of this method is to be able to change the default configuration\n     * of the detailed week view in the preview pane. Especially being able to show\n     * all the hours in the print view\n     * \n     * @param newDetailedWeekView\n     *            view.\n     * @param trimTimeBounds\n     *            define if trim or not the hours in the week view\n     */\n    protected void configureDetailedWeekView(DetailedWeekView newDetailedWeekView, boolean trimTimeBounds) {\n        newDetailedWeekView.getWeekView().setShowToday(false);\n        newDetailedWeekView.getWeekView().setTrimTimeBounds(trimTimeBounds);\n    }\n\n    /**\n     * Default configuration for Month view in the preview pane.\n     * \n     * @return the month view\n     */\n    protected MonthView createMonthView() {\n        MonthView newMonthView = new MonthView();\n        newMonthView.setShowToday(false);\n        newMonthView.setShowCurrentWeek(false);\n        newMonthView.weekFieldsProperty().bind(weekFieldsProperty());\n        newMonthView.showFullDayEntriesProperty().bind(showAllDayEntriesProperty());\n        newMonthView.showTimedEntriesProperty().bind(showTimedEntriesProperty());\n        newMonthView.addEventFilter(MouseEvent.ANY, weakMouseHandler);\n        newMonthView.dateProperty().bind(pageStartDateProperty());\n        newMonthView.zoneIdProperty().bind(zoneIdProperty());\n        return newMonthView;\n    }\n\n    private static final class PrintPeriodSplitter implements InvalidationListener {\n\n        private final PrintablePage page;\n        private PageSlice slice;\n\n        public PrintPeriodSplitter(PrintablePage page) {\n            this.page = Objects.requireNonNull(page);\n            page.viewTypeProperty().addListener(this);\n            page.printStartDateProperty().addListener(this);\n            page.printEndDateProperty().addListener(this);\n            split();\n        }\n\n        @Override\n        public void invalidated(Observable observable) {\n            split();\n        }\n\n        private void split() {\n            LocalDate printStart = page.getPrintStartDate();\n            LocalDate printEnd = page.getPrintEndDate();\n\n            if (printStart == null || printEnd == null || printStart.isAfter(printEnd)) {\n                // Just in case, should never happen!\n                return;\n            }\n\n            PageSlice first = null;\n            PageSlice pivot = null;\n            LocalDate pageStartDate = printStart;\n            int count = 0;\n\n            do {\n                LocalDate pageEndDate = pageStartDate.plus(1, page.getViewType().getChronoUnit()).minusDays(1);\n\n                PageSlice next = new PageSlice(++count, pageStartDate, pageEndDate);\n\n                if (first == null) {\n                    first = next;\n                }\n\n                if (pivot != null) {\n                    pivot.setNext(next);\n                }\n\n                pivot = next;\n                pageStartDate = pageEndDate.plusDays(1);\n            } while (pageStartDate.isBefore(printEnd) || pageStartDate.isEqual(printEnd));\n\n            setSlice(first);\n            page.setTotalPages(count);\n        }\n\n        public boolean next() {\n            if (slice != null && slice.hasNext()) {\n                setSlice(slice.getNext());\n                return true;\n            }\n            return false;\n        }\n\n        public boolean back() {\n            if (slice != null && slice.hasBack()) {\n                setSlice(slice.getBack());\n            }\n            return false;\n        }\n\n        private void setSlice(PageSlice slice) {\n            this.slice = slice;\n            this.page.setPageStartDate(slice.getStart());\n            this.page.setPageEndDate(slice.getEnd());\n            this.page.setPageNumber(slice.getNumber());\n        }\n\n    }\n\n    private static final class PageSlice {\n\n        private final int number;\n        private final LocalDate start;\n        private final LocalDate end;\n        private PageSlice next;\n        private PageSlice back;\n\n        public PageSlice(int number, LocalDate start, LocalDate end) {\n            this.number = number;\n            this.start = start;\n            this.end = end;\n        }\n\n        public int getNumber() {\n            return number;\n        }\n\n        public LocalDate getStart() {\n            return start;\n        }\n\n        public LocalDate getEnd() {\n            return end;\n        }\n\n        public PageSlice getNext() {\n            return next;\n        }\n\n        public void setNext(PageSlice next) {\n            if (this.next != next) {\n                this.next = next;\n                next.setBack(this);\n            }\n        }\n\n        public PageSlice getBack() {\n            return back;\n        }\n\n        public void setBack(PageSlice back) {\n            if (this.back != back) {\n                this.back = back;\n                back.setNext(this);\n            }\n        }\n\n        public boolean hasNext() {\n            return next != null;\n        }\n\n        public boolean hasBack() {\n            return back != null;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/SettingsView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.view.CalendarFXControl;\nimport com.calendarfx.view.SourceView;\nimport impl.com.calendarfx.view.print.SettingsViewSkin;\nimport javafx.scene.control.Skin;\n\n/**\n * The right-hand side of the {@link PrintView}. This view combines several controls\n * found in the print package. The view contains the following sub-controls in this\n * order from top to bottom.\n * <ul>\n *     <li>PaperView - for setting the paper size, the view type (day, week, month) and to specify print margins.</li>\n *     <li>TimeRangeView - for specifying the time range that shall be printed (e.g. \"today\", \"tomorrow\", \"next 5 months\", etc...</li>\n *     <li>SourceView - for displaying the calendars that are used for printing. Calendar visibility can be toggled.</li>\n *     <li>OptionsView - for enabling / disabling various visualizations (e.g. swimlane layout).</li>\n * </ul>\n * The default style of this control is \"print-settings-view\".\n *\n * <img src=\"doc-files/settings-view.png\" alt=\"Settings View\">\n */\npublic class SettingsView extends CalendarFXControl {\n\n    public static final String DEFAULT_STYLE = \"print-settings-view\";\n\n    private final PaperView paperView;\n    private final TimeRangeView timeRangeView;\n    private final SourceView sourceView;\n    private final OptionsView optionsView;\n\n    public SettingsView() {\n        super();\n\n        getStyleClass().add(DEFAULT_STYLE);\n\n        paperView = new PaperView();\n        timeRangeView = new TimeRangeView();\n        sourceView = new SourceView();\n        optionsView = new OptionsView();\n\n        timeRangeView.viewTypeProperty().bind(paperView.viewTypeProperty());\n        optionsView.viewTypeProperty().bind(paperView.viewTypeProperty());\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new SettingsViewSkin(this);\n    }\n\n    public final PaperView getPaperView() {\n        return paperView;\n    }\n\n    public final TimeRangeView getTimeRangeView() {\n        return timeRangeView;\n    }\n\n    public final OptionsView getOptionsView() {\n        return optionsView;\n    }\n\n    public final SourceView getSourceView() {\n        return sourceView;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/TimeRangeField.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport impl.com.calendarfx.view.print.TimeRangeFieldSkin;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Skin;\nimport org.controlsfx.control.PropertySheet.Item;\n\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.time.temporal.WeekFields;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Optional;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A control for specifying the start or end time of a time range. This control\n * is used by the {@link TimeRangeView} as part of the print preview\n * functionality. It allows the user to specify the time range that has to be\n * printed. The control supports defining time points for day ranges, week\n * rangers, or month ranges. The default style class used by this control is\n * \"time-range-field\".\n *\n * <img src=\"doc-files/time-range-field.png\" alt=\"Time Range Field\">\n */\npublic class TimeRangeField extends ViewTypeControl {\n\n    public static final String DEFAULT_STYLE = \"time-range-field\";\n\n    public TimeRangeField() {\n        this(ViewType.DAY_VIEW);\n    }\n\n    public TimeRangeField(ViewType viewType) {\n        this(viewType, false);\n    }\n\n    public TimeRangeField(boolean endField) {\n        this(ViewType.DAY_VIEW, endField);\n    }\n\n    public TimeRangeField(ViewType viewType, boolean endField) {\n        getStyleClass().add(DEFAULT_STYLE);\n\n        InvalidationListener updateValuesListener = obs -> updateValues();\n        viewTypeProperty().addListener(updateValuesListener);\n        endFieldProperty().addListener(updateValuesListener);\n\n        valueProperty().addListener(obs -> updateFields());\n\n        setViewType(viewType);\n        setEndField(endField);\n\n        updateValues();\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new TimeRangeFieldSkin(this);\n    }\n\n    private final ObjectProperty<LocalDate> today = new SimpleObjectProperty<>(\n            this, \"today\", LocalDate.now());\n\n    /**\n     * Stores the date that is considered to represent \"today\". This property is\n     * initialized with {@link LocalDate#now()} but can be any date.\n     *\n     * @return the date representing \"today\"\n     */\n    public final ObjectProperty<LocalDate> todayProperty() {\n        return today;\n    }\n\n    /**\n     * Sets the value of {@link #todayProperty()}.\n     *\n     * @param date\n     *            the date representing \"today\"\n     */\n    public final void setToday(LocalDate date) {\n        requireNonNull(date);\n        todayProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #todayProperty()}.\n     *\n     * @return the date representing \"today\"\n     */\n    public final LocalDate getToday() {\n        return todayProperty().get();\n    }\n\n    private final ObjectProperty<WeekFields> weekFields = new SimpleObjectProperty<>(\n            this, \"weekFields\", WeekFields.ISO);\n\n    /**\n     * Week fields are used to determine the first day of a week (e.g. \"Monday\"\n     * in Germany or \"Sunday\" in the US). It is also used to calculate the week\n     * number as the week fields determine how many days are needed in the first\n     * week of a year. This property is initialized with {@link WeekFields#ISO}.\n     *\n     * @return the week fields\n     */\n    public final ObjectProperty<WeekFields> weekFieldsProperty() {\n        return weekFields;\n    }\n\n    /**\n     * Sets the value of {@link #weekFieldsProperty()}.\n     *\n     * @param fields\n     *            the new week fields\n     */\n    public final void setWeekFields(WeekFields fields) {\n        requireNonNull(fields);\n        weekFieldsProperty().set(fields);\n    }\n\n    /**\n     * Returns the value of {@link #weekFieldsProperty()}.\n     *\n     * @return the week fields\n     */\n    public final WeekFields getWeekFields() {\n        return weekFieldsProperty().get();\n    }\n\n    private void updateValues() {\n        values.setAll(TimeRangeFieldValue.getSelectablesForView(getViewType(),\n                isEndField()));\n        setValue(values.get(0));\n    }\n\n    private void updateFields() {\n        if (getValue() == TimeRangeFieldValue.ON_DATE) {\n            if (getOnDate() == null) {\n                setOnDate(getToday());\n            }\n            setOnWeekNumber(null);\n            setMonthYear(null);\n            setAfterUnits(null);\n        } else if (getValue() == TimeRangeFieldValue.ON_WEEK_NUMBER) {\n            if (getOnWeekNumber() == null) {\n                setOnWeekNumber(\n                        getToday().get(getWeekFields().weekOfWeekBasedYear()));\n            }\n            setOnDate(null);\n            setMonthYear(null);\n            setAfterUnits(null);\n        } else if (getValue() == TimeRangeFieldValue.AFTER) {\n            if (getAfterUnits() == null) {\n                setAfterUnits(1);\n            }\n            setOnDate(null);\n            setOnWeekNumber(null);\n            setMonthYear(null);\n        } else if (getValue().isMonthValue()) {\n            if (getMonthYear() == null) {\n                setMonthYear(getToday().getYear());\n            }\n            setOnDate(null);\n            setOnWeekNumber(null);\n            setAfterUnits(null);\n        } else {\n            setOnDate(null);\n            setOnWeekNumber(null);\n            setMonthYear(null);\n            setAfterUnits(null);\n        }\n    }\n\n    // end field support\n\n    private final BooleanProperty endField = new SimpleBooleanProperty(this,\n            \"endField\");\n\n    public final BooleanProperty endFieldProperty() {\n        return endField;\n    }\n\n    public final boolean isEndField() {\n        return endFieldProperty().get();\n    }\n\n    public final void setEndField(boolean endField) {\n        endFieldProperty().set(endField);\n    }\n\n    private final ObservableList<TimeRangeFieldValue> values = FXCollections\n            .observableArrayList();\n\n    private final ObservableList<TimeRangeFieldValue> valuesUnmodifiable = FXCollections\n            .unmodifiableObservableList(values);\n\n    public ObservableList<TimeRangeFieldValue> getValues() {\n        return valuesUnmodifiable;\n    }\n\n    // value support\n\n    private final ObjectProperty<TimeRangeFieldValue> value = new SimpleObjectProperty<TimeRangeFieldValue>(\n            this, \"value\") {\n        @Override\n        public void set(TimeRangeFieldValue newValue) {\n            if (newValue == null)\n                return;\n            super.set(newValue);\n        }\n    };\n\n    public final ObjectProperty<TimeRangeFieldValue> valueProperty() {\n        return value;\n    }\n\n    public final TimeRangeFieldValue getValue() {\n        return valueProperty().get();\n    }\n\n    public final void setValue(TimeRangeFieldValue value) {\n        valueProperty().set(value);\n    }\n\n    // on date support\n\n    private final ObjectProperty<LocalDate> onDate = new SimpleObjectProperty<>(\n            this, \"onDate\");\n\n    public final ObjectProperty<LocalDate> onDateProperty() {\n        return onDate;\n    }\n\n    public final LocalDate getOnDate() {\n        return onDateProperty().get();\n    }\n\n    public final void setOnDate(LocalDate onDate) {\n        onDateProperty().set(onDate);\n    }\n\n    // on week support\n\n    private final ObjectProperty<Integer> onWeekNumber = new SimpleObjectProperty<>(\n            this, \"onWeekNumber\");\n\n    public final ObjectProperty<Integer> onWeekNumberProperty() {\n        return onWeekNumber;\n    }\n\n    public final Integer getOnWeekNumber() {\n        return onWeekNumberProperty().get();\n    }\n\n    public final void setOnWeekNumber(Integer onWeekNumber) {\n        onWeekNumberProperty().set(onWeekNumber);\n    }\n\n    // month year support\n\n    private final ObjectProperty<Integer> monthYear = new SimpleObjectProperty<>(\n            this, \"monthYear\");\n\n    public final ObjectProperty<Integer> monthYearProperty() {\n        return monthYear;\n    }\n\n    public final Integer getMonthYear() {\n        return monthYearProperty().get();\n    }\n\n    public final void setMonthYear(Integer monthYear) {\n        monthYearProperty().set(monthYear);\n    }\n\n    // after units support\n\n    private final ObjectProperty<Integer> afterUnits = new SimpleObjectProperty<>(\n            this, \"afterUnits\");\n\n    public final ObjectProperty<Integer> afterUnitsProperty() {\n        return afterUnits;\n    }\n\n    public final Integer getAfterUnits() {\n        return afterUnitsProperty().get();\n    }\n\n    public final void setAfterUnits(Integer afterUnits) {\n        afterUnitsProperty().set(afterUnits);\n    }\n\n    public enum TimeRangeFieldValue {\n\n        TODAY(1, \"TimeRangeFieldValue.TODAY_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.DAY_VIEW);\n            }\n        },\n\n        TOMORROW(2, \"TimeRangeFieldValue.TOMORROW\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.DAY_VIEW);\n            }\n        },\n\n        THIS_WEEK(3, \"TimeRangeFieldValue.THIS_WEEK_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.WEEK_VIEW);\n            }\n        },\n\n        NEXT_WEEK(4, \"TimeRangeFieldValue.NEXT_WEEK_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.WEEK_VIEW);\n            }\n        },\n\n        ON_WEEK_NUMBER(5, \"TimeRangeFieldValue.ON_WEEK_NUMBER_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.WEEK_VIEW);\n            }\n        },\n\n        THIS_MONTH(6, \"TimeRangeFieldValue.THIS_MONTH_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        NEXT_MONTH(7, \"TimeRangeFieldValue.NEXT_MONTH_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        JANUARY(8, \"TimeRangeFieldValue.JANUARY_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        FEBRUARY(9, \"TimeRangeFieldValue.FEBRUARY_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        MARCH(10, \"TimeRangeFieldValue.MARCH_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        APRIL(11, \"TimeRangeFieldValue.APRIL_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        MAY(12, \"TimeRangeFieldValue.MAY_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        JUNE(13, \"TimeRangeFieldValue.JUNE_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        JULY(14, \"TimeRangeFieldValue.JULY_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        AUGUST(15, \"TimeRangeFieldValue.AUGUST_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        SEPTEMBER(16, \"TimeRangeFieldValue.SEPTEMBER_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        OCTOBER(17, \"TimeRangeFieldValue.OCTOBER_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        NOVEMBER(18, \"TimeRangeFieldValue.NOVEMBER_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        DECEMBER(19, \"TimeRangeFieldValue.DECEMBER_LABEL\") {\n            @Override\n            public boolean isMonthValue() {\n                return true;\n            }\n\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Collections.singletonList(ViewType.MONTH_VIEW);\n            }\n        },\n\n        ON_DATE(20, \"TimeRangeFieldValue.ON_DATE_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Arrays.asList(ViewType.DAY_VIEW, ViewType.WEEK_VIEW);\n            }\n        },\n\n        AFTER(21, \"TimeRangeFieldValue.AFTER_LABEL\") {\n            @Override\n            public Collection<ViewType> getViewTypes() {\n                return Arrays.asList(ViewType.DAY_VIEW, ViewType.WEEK_VIEW,\n                        ViewType.MONTH_VIEW);\n            }\n        };\n\n        private final int order;\n        private final String messageKey;\n\n        TimeRangeFieldValue(int order, String messageKey) {\n            this.order = order;\n            this.messageKey = messageKey;\n        }\n\n        public String getMessageKey() {\n            return messageKey;\n        }\n\n        public boolean isMonthValue() {\n            return false;\n        }\n\n        public boolean isSelectableForView(ViewType viewType) {\n            return getViewTypes().contains(viewType);\n        }\n\n        public abstract Collection<ViewType> getViewTypes();\n\n        public static List<TimeRangeFieldValue> getSelectablesForView(\n                ViewType viewType, boolean endField) {\n            List<TimeRangeFieldValue> allowedValues = new ArrayList<>();\n\n            for (TimeRangeFieldValue value : values()) {\n                if (value.isSelectableForView(viewType)) {\n                    allowedValues.add(value);\n                }\n            }\n\n            if (!endField) {\n                allowedValues.remove(AFTER);\n            }\n\n            allowedValues.sort(Comparator.comparingInt(v -> v.order));\n\n            return allowedValues;\n        }\n\n        public static TimeRangeFieldValue getFromMonth(Month month) {\n            switch (month) {\n            case JANUARY:\n                return TimeRangeFieldValue.JANUARY;\n\n            case FEBRUARY:\n                return TimeRangeFieldValue.FEBRUARY;\n\n            case MARCH:\n                return TimeRangeFieldValue.MARCH;\n\n            case APRIL:\n                return TimeRangeFieldValue.APRIL;\n\n            case MAY:\n                return TimeRangeFieldValue.MAY;\n\n            case JUNE:\n                return TimeRangeFieldValue.JUNE;\n\n            case JULY:\n                return TimeRangeFieldValue.JULY;\n\n            case AUGUST:\n                return TimeRangeFieldValue.AUGUST;\n\n            case SEPTEMBER:\n                return TimeRangeFieldValue.SEPTEMBER;\n\n            case OCTOBER:\n                return TimeRangeFieldValue.OCTOBER;\n\n            case NOVEMBER:\n                return TimeRangeFieldValue.NOVEMBER;\n\n            default:\n                return TimeRangeFieldValue.DECEMBER;\n            }\n        }\n    }\n\n    private static final String TIME_RANGE_FIELD_CATEGORY = \"Time Range Field\";\n\n    @Override\n    public ObservableList<Item> getPropertySheetItems() {\n        ObservableList<Item> items = super.getPropertySheetItems();\n\n        items.add(new Item() {\n            @Override\n            public void setValue(Object value) {\n                setEndField((boolean) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return isEndField();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return Boolean.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(endFieldProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"End Field\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Indicates this field is used for te end value\";\n            }\n\n            @Override\n            public String getCategory() {\n                return TIME_RANGE_FIELD_CATEGORY;\n            }\n        });\n\n        return items;\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/TimeRangeView.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\n\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.print.TimeRangeField.TimeRangeFieldValue;\nimport impl.com.calendarfx.view.print.TimeRangeViewSkin;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyIntegerProperty;\nimport javafx.beans.property.ReadOnlyIntegerWrapper;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\n\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Month;\nimport java.time.YearMonth;\nimport java.time.temporal.ChronoField;\nimport java.time.temporal.WeekFields;\nimport java.util.EnumMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A control for specifying the start and end time of a time range. This control\n * is used as part of the print preview functionality. It allows the user to\n * specify the time range that has to be printed. The control supports day\n * ranges, week rangers, or month ranges. The default style class used by this\n * control is \"time-range-view\".\n *\n * <img src=\"doc-files/time-range-view.png\" alt=\"Time Range View\">\n */\npublic class TimeRangeView extends ViewTypeControl {\n\n    private static final String DEFAULT_STYLE = \"time-range-view\";\n\n    private final TimeRangeField startField = new TimeRangeField();\n\n    private final TimeRangeField endField = new TimeRangeField(true);\n\n    public final Map<ViewType, TimeRangeOldValues> oldValuesMap = new EnumMap<>(ViewType.class);\n\n    public final InvalidationListener OldValuesListener = obs -> catchOldValues();\n\n    /**\n     * Constructs a new time range view.\n     */\n    public TimeRangeView() {\n\n        viewTypeProperty().addListener(obs -> {\n            startDate.removeListener(OldValuesListener);\n            endDate.removeListener(OldValuesListener);\n        });\n\n        startField.viewTypeProperty().bind(viewTypeProperty());\n        startField.todayProperty().bind(todayProperty());\n        startField.weekFieldsProperty().bind(weekFieldsProperty());\n\n        InvalidationListener startDateUpdater = obs -> updateStartDate();\n        startField.valueProperty().addListener(startDateUpdater);\n        startField.onDateProperty().addListener(startDateUpdater);\n        startField.onWeekNumberProperty().addListener(startDateUpdater);\n        startField.monthYearProperty().addListener(startDateUpdater);\n        startField.afterUnitsProperty().addListener(startDateUpdater);\n        startField.todayProperty().addListener(startDateUpdater);\n\n        endField.viewTypeProperty().bind(viewTypeProperty());\n        endField.todayProperty().bind(todayProperty());\n        endField.weekFieldsProperty().bind(weekFieldsProperty());\n\n        InvalidationListener endDateUpdater = obs -> updateEndDate();\n        endField.valueProperty().addListener(endDateUpdater);\n        endField.onDateProperty().addListener(endDateUpdater);\n        endField.onWeekNumberProperty().addListener(endDateUpdater);\n        endField.monthYearProperty().addListener(endDateUpdater);\n        endField.afterUnitsProperty().addListener(endDateUpdater);\n        endField.todayProperty().addListener(endDateUpdater);\n\n        startDate.addListener(obs -> {\n            fixEndField();\n            updateEndDate();\n            updateUnitsToPrint();\n        });\n\n        endDate.addListener(obs -> {\n            fixStartField();\n            updateUnitsToPrint();\n        });\n\n        oldValuesMap.put(ViewType.DAY_VIEW, null);\n        oldValuesMap.put(ViewType.WEEK_VIEW, null);\n        oldValuesMap.put(ViewType.MONTH_VIEW, null);\n\n        getStyleClass().add(DEFAULT_STYLE);\n        updateStartDate();\n        updateEndDate();\n        updateUnitsToPrint();\n    }\n\n    private final ObjectProperty<LocalDate> today = new SimpleObjectProperty<>(this, \"today\", LocalDate.now());\n\n    /**\n     * Stores the date that is considered to represent \"today\". This property is\n     * initialized with {@link LocalDate#now()} but can be any date.\n     *\n     * @return the date representing \"today\"\n     */\n    public final ObjectProperty<LocalDate> todayProperty() {\n        return today;\n    }\n\n    /**\n     * Sets the value of {@link #todayProperty()}.\n     *\n     * @param date the date representing \"today\"\n     */\n    public final void setToday(LocalDate date) {\n        requireNonNull(date);\n        todayProperty().set(date);\n    }\n\n    /**\n     * Returns the value of {@link #todayProperty()}.\n     *\n     * @return the date representing \"today\"\n     */\n    public final LocalDate getToday() {\n        return todayProperty().get();\n    }\n\n    private final ObjectProperty<WeekFields> weekFields = new SimpleObjectProperty<>(this, \"weekFields\", WeekFields.ISO);\n\n    /**\n     * Week fields are used to determine the first day of a week (e.g. \"Monday\"\n     * in Germany or \"Sunday\" in the US). It is also used to calculate the week\n     * number as the week fields determine how many days are needed in the first\n     * week of a year. This property is initialized with {@link WeekFields#ISO}.\n     *\n     * @return the week fields\n     */\n    public final ObjectProperty<WeekFields> weekFieldsProperty() {\n        return weekFields;\n    }\n\n    /**\n     * Sets the value of {@link #weekFieldsProperty()}.\n     *\n     * @param fields the new week fields\n     */\n    public final void setWeekFields(WeekFields fields) {\n        requireNonNull(fields);\n        weekFieldsProperty().set(fields);\n    }\n\n    /**\n     * Returns the value of {@link #weekFieldsProperty()}.\n     *\n     * @return the week fields\n     */\n    public final WeekFields getWeekFields() {\n        return weekFieldsProperty().get();\n    }\n\n\n    /**\n     * Set the expected values for TimeRange combos. <br>\n     * If dialog is opened for the first time or user hasn't made any change\n     * It will calculate the values based on {@link CalendarView#dateProperty()}.\n     * Otherwise, It will search on OldValuesMap in order to mantain in combos\n     * what user does while dialog is opened. <br>\n     * <p>\n     * When dialog get closed, old values get cleaned.\n     *\n     * @param date Used to calculate values of combos when dialog is\n     *             opened for the first time.\n     */\n    public final void loadDropDownValues(LocalDate date) {\n        TimeRangeOldValues oldValue = oldValuesMap.get(getViewType());\n        if (date == null) {\n            return;\n        }\n\n        switch (getViewType()) {\n            case DAY_VIEW:\n\n                // Will enter here if user has made some changes in Time range\n                // combos for DAY_VIEW type.\n                if (oldValue != null) {\n\n                    startField.setValue(oldValue.startField.getValue());\n                    endField.setValue(oldValue.endField.getValue());\n\n                    if (startField.getValue() == TimeRangeFieldValue.ON_DATE) {\n                        startField.setOnDate(oldValue.startField.getOnDate());\n                    }\n\n                    if (endField.getValue() == TimeRangeFieldValue.ON_DATE) {\n                        endField.setOnDate(oldValue.endField.getOnDate());\n                    } else if (endField.getValue() == TimeRangeFieldValue.AFTER) {\n                        endField.setAfterUnits(oldValue.endField.getAfterUnits());\n                    }\n                } else {\n\n                    if (date.equals(getToday())) {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.TODAY);\n                    } else if (date.equals(getTomorrow())) {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.TOMORROW);\n                    } else {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.ON_DATE);\n                        startField.setOnDate(date);\n                    }\n                    endField.setValue(TimeRangeField.TimeRangeFieldValue.AFTER);\n                }\n                break;\n\n            case WEEK_VIEW:\n\n                // Will enter here if user has made some changes in Time range\n                // combos for WEEK_VIEW type.\n                if (oldValue != null) {\n\n                    startField.setValue(oldValue.startField.getValue());\n                    endField.setValue(oldValue.endField.getValue());\n\n                    if (startField.getValue() == TimeRangeFieldValue.ON_DATE) {\n                        startField.setOnDate(oldValue.startField.getOnDate());\n                    } else if (startField.getValue() == TimeRangeFieldValue.ON_WEEK_NUMBER) {\n                        startField.setOnWeekNumber(oldValue.startField.getOnWeekNumber());\n                    }\n\n                    if (endField.getValue() == TimeRangeFieldValue.ON_DATE) {\n                        endField.setOnDate(oldValue.endField.getOnDate());\n                    } else if (endField.getValue() == TimeRangeFieldValue.ON_WEEK_NUMBER) {\n                        endField.setOnWeekNumber(oldValue.endField.getOnWeekNumber());\n                    } else if (endField.getValue() == TimeRangeFieldValue.AFTER) {\n                        endField.setAfterUnits(oldValue.endField.getAfterUnits());\n                    }\n                } else {\n                    if (isCurrentWeek(date)) {\n                        startField\n                                .setValue(TimeRangeField.TimeRangeFieldValue.THIS_WEEK);\n                    } else if (isNextWeek(date)) {\n                        startField\n                                .setValue(TimeRangeField.TimeRangeFieldValue.NEXT_WEEK);\n                    } else {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.ON_DATE);\n                        startField.setOnDate(date);\n                    }\n                    endField.setValue(TimeRangeFieldValue.AFTER);\n                }\n                break;\n\n            case MONTH_VIEW:\n\n                // Will enter here if user has made some changes in Time range\n                // combos for MONTH_VIEW type.\n                if (oldValue != null) {\n\n                    startField.setValue(oldValue.startField.getValue());\n                    endField.setValue(oldValue.endField.getValue());\n\n                    if (startField.getValue() != TimeRangeField.TimeRangeFieldValue.THIS_MONTH\n                            && startField.getValue() != TimeRangeField.TimeRangeFieldValue.NEXT_MONTH) {\n                        startField.setMonthYear(oldValue.startField.getMonthYear());\n                    }\n\n                    if (endField.getValue() == TimeRangeFieldValue.AFTER) {\n                        endField.setAfterUnits(oldValue.endField.getAfterUnits());\n                    } else if (endField.getValue() != TimeRangeField.TimeRangeFieldValue.THIS_MONTH\n                            && startField.getValue() != TimeRangeField.TimeRangeFieldValue.NEXT_MONTH) {\n                        endField.setMonthYear(oldValue.endField.getMonthYear());\n                    }\n                } else {\n                    if (isCurrentMonth(date)) {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.THIS_MONTH);\n                    } else if (isNextMonth(date)) {\n                        startField.setValue(TimeRangeField.TimeRangeFieldValue.NEXT_MONTH);\n                    } else {\n                        TimeRangeField.TimeRangeFieldValue month = TimeRangeField.TimeRangeFieldValue\n                                .getFromMonth(date.getMonth());\n                        int year = date.getYear();\n                        startField.setValue(month);\n                        startField.setMonthYear(year);\n                    }\n                    endField.setValue(TimeRangeFieldValue.AFTER);\n                }\n                break;\n\n            default:\n                throw new UnsupportedOperationException(\n                        \"Not supported yet!: \" + getViewType());\n        }\n\n        startDate.addListener(OldValuesListener);\n        endDate.addListener(OldValuesListener);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new TimeRangeViewSkin(this);\n    }\n\n    public final TimeRangeField getStartField() {\n        return startField;\n    }\n\n    public final TimeRangeField getEndField() {\n        return endField;\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> startDate = new ReadOnlyObjectWrapper<>(this, \"startDate\");\n\n    public final ReadOnlyObjectProperty<LocalDate> startDateProperty() {\n        return startDate.getReadOnlyProperty();\n    }\n\n    public final LocalDate getStartDate() {\n        return startDate.get();\n    }\n\n    private void setStartDate(LocalDate startDate) {\n        if (!Objects.requireNonNull(startDate).equals(getStartDate())) {\n            this.startDate.set(startDate);\n        }\n    }\n\n    private LocalDate getTomorrow() {\n        return getToday().plusDays(1);\n    }\n\n    private LocalDate getThisWeekDate() {\n        return getStartOfWeek(getToday());\n    }\n\n    private LocalDate getNextWeekDate() {\n        return getThisWeekDate().plusWeeks(1);\n    }\n\n    private LocalDate getStartOfWeek(LocalDate date) {\n        return Util.adjustToFirstDayOfWeek(date, getWeekFields().getFirstDayOfWeek());\n    }\n\n    private LocalDate getEndOfWeek(LocalDate date) {\n        return Util.adjustToLastDayOfWeek(date, getWeekFields().getFirstDayOfWeek());\n    }\n\n    private boolean isCurrentWeek(LocalDate date) {\n        return !date.isBefore(getStartOfWeek(getToday())) && !date.isAfter(getEndOfWeek(getToday()));\n    }\n\n    private boolean isNextWeek(LocalDate date) {\n        return !date.isBefore(getStartOfWeek(getNextWeekDate())) && !date.isAfter(getEndOfWeek(getNextWeekDate()));\n    }\n\n    private LocalDate getThisMonthDate() {\n        return getStartOfMonth(getToday());\n    }\n\n    private LocalDate getNextMonthDate() {\n        return getThisMonthDate().plusMonths(1);\n    }\n\n    private LocalDate getStartOfMonth(LocalDate date) {\n        return YearMonth.of(date.getYear(), date.getMonthValue()).atDay(1);\n    }\n\n    private LocalDate getEndOfMonth(LocalDate date) {\n        return YearMonth.of(date.getYear(), date.getMonthValue()).atEndOfMonth();\n    }\n\n    private boolean isCurrentMonth(LocalDate date) {\n        return date.getYear() == getToday().getYear() && date.getMonth().equals(getToday().getMonth());\n    }\n\n    private boolean isNextMonth(LocalDate date) {\n        return date.getYear() == getToday().getYear() && date.getMonth().equals(getNextMonthDate().getMonth());\n    }\n\n    private void updateStartDate() {\n        switch (startField.getValue()) {\n            case TODAY:\n                setStartDate(getToday());\n                break;\n\n            case TOMORROW:\n                setStartDate(getTomorrow());\n                break;\n\n            case ON_DATE:\n                if (getViewType() == ViewType.DAY_VIEW) {\n                    setStartDate(startField.getOnDate());\n                } else {\n                    setStartDate(getStartOfWeek(startField.getOnDate()));\n                }\n                break;\n\n            case THIS_WEEK:\n                setStartDate(getThisWeekDate());\n                break;\n\n            case NEXT_WEEK:\n                setStartDate(getNextWeekDate());\n                break;\n\n            case ON_WEEK_NUMBER:\n                int startWeekNumber = getToday().get(getWeekFields().weekOfWeekBasedYear());\n                int diff = startField.getOnWeekNumber() - startWeekNumber;\n                setStartDate(getToday().plusWeeks(diff).with(ChronoField.DAY_OF_WEEK, getWeekFields().getFirstDayOfWeek().getValue()));\n                break;\n\n            case THIS_MONTH:\n                setStartDate(getThisMonthDate());\n                break;\n\n            case NEXT_MONTH:\n                setStartDate(getNextMonthDate());\n                break;\n\n            case JANUARY:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.JANUARY.getValue()).atDay(1));\n                break;\n\n            case FEBRUARY:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.FEBRUARY.getValue()).atDay(1));\n                break;\n\n            case MARCH:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.MARCH.getValue()).atDay(1));\n                break;\n\n            case APRIL:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.APRIL.getValue()).atDay(1));\n                break;\n\n            case MAY:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.MAY.getValue()).atDay(1));\n                break;\n\n            case JUNE:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.JUNE.getValue()).atDay(1));\n                break;\n\n            case JULY:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.JULY.getValue()).atDay(1));\n                break;\n\n            case AUGUST:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.AUGUST.getValue()).atDay(1));\n                break;\n\n            case SEPTEMBER:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.SEPTEMBER.getValue()).atDay(1));\n                break;\n\n            case OCTOBER:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.OCTOBER.getValue()).atDay(1));\n                break;\n\n            case NOVEMBER:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.NOVEMBER.getValue()).atDay(1));\n                break;\n\n            case DECEMBER:\n                setStartDate(YearMonth.of(startField.getMonthYear(), Month.DECEMBER.getValue()).atDay(1));\n                break;\n\n            default:\n                throw new UnsupportedOperationException(\"Value not supported!\");\n        }\n    }\n\n    private final ReadOnlyObjectWrapper<LocalDate> endDate = new ReadOnlyObjectWrapper<>(this, \"endDate\");\n\n    public final ReadOnlyObjectProperty<LocalDate> endDateProperty() {\n        return endDate.getReadOnlyProperty();\n    }\n\n    public final LocalDate getEndDate() {\n        return endDate.get();\n    }\n\n    private void setEndDate(LocalDate endDate) {\n        if (!Objects.requireNonNull(endDate).equals(getEndDate())) {\n            this.endDate.set(endDate);\n        }\n    }\n\n    private void updateEndDate() {\n        switch (endField.getValue()) {\n            case TODAY:\n                setEndDate(getToday());\n                break;\n\n            case TOMORROW:\n                setEndDate(getTomorrow());\n                break;\n\n            case ON_DATE:\n                if (getViewType() == ViewType.DAY_VIEW) {\n                    setEndDate(endField.getOnDate());\n                } else {\n                    setEndDate(getEndOfWeek(endField.getOnDate()));\n                }\n                break;\n\n            case THIS_WEEK:\n                setEndDate(getEndOfWeek(getToday()));\n                break;\n\n            case NEXT_WEEK:\n                setEndDate(getEndOfWeek(getToday()).plusWeeks(1));\n                break;\n\n            case ON_WEEK_NUMBER:\n                int startWeekNumber = getToday().get(getWeekFields().weekOfWeekBasedYear());\n                int difference = endField.getOnWeekNumber() - startWeekNumber;\n                setEndDate(getToday().plusWeeks(difference).with(ChronoField.DAY_OF_WEEK, getWeekFields().getFirstDayOfWeek().plus(6).getValue()));\n                break;\n\n            case THIS_MONTH:\n                setEndDate(getEndOfMonth(getToday()));\n                break;\n\n            case NEXT_MONTH:\n                setEndDate(getEndOfMonth(getToday()).plusMonths(1));\n                break;\n\n            case JANUARY:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.JANUARY.getValue()).atEndOfMonth());\n                break;\n\n            case FEBRUARY:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.FEBRUARY.getValue()).atEndOfMonth());\n                break;\n\n            case MARCH:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.MARCH.getValue()).atEndOfMonth());\n                break;\n\n            case APRIL:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.APRIL.getValue()).atEndOfMonth());\n                break;\n\n            case MAY:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.MAY.getValue()).atEndOfMonth());\n                break;\n\n            case JUNE:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.JUNE.getValue()).atEndOfMonth());\n                break;\n\n            case JULY:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.JULY.getValue()).atEndOfMonth());\n                break;\n\n            case AUGUST:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.AUGUST.getValue()).atEndOfMonth());\n                break;\n\n            case SEPTEMBER:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.SEPTEMBER.getValue()).atEndOfMonth());\n                break;\n\n            case OCTOBER:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.OCTOBER.getValue()).atEndOfMonth());\n                break;\n\n            case NOVEMBER:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.NOVEMBER.getValue()).atEndOfMonth());\n                break;\n\n            case DECEMBER:\n                setEndDate(YearMonth.of(endField.getMonthYear(), Month.DECEMBER.getValue()).atEndOfMonth());\n                break;\n\n            case AFTER:\n                int units = endField.getAfterUnits();\n                if (getViewType() == ViewType.DAY_VIEW) {\n                    setEndDate(getStartDate().plusDays(units - 1L));\n                } else if (getViewType() == ViewType.WEEK_VIEW) {\n                    setEndDate(getEndOfWeek(getStartDate()).plusWeeks(units - 1L));\n                } else {\n                    setEndDate(getEndOfMonth(getStartDate()).plusMonths(units - 1L));\n                }\n                break;\n\n            default:\n                throw new UnsupportedOperationException(\"Value not supported!\");\n        }\n    }\n\n    private void fixEndField() {\n        LocalDate mStartDate = getStartDate();\n        LocalDate mEndDate = getEndDate();\n\n        if (mStartDate == null || mEndDate == null) {\n            return;\n        }\n\n        mEndDate = endField.getValue() == TimeRangeFieldValue.AFTER ? mStartDate : mEndDate;\n\n        if (mEndDate.isBefore(mStartDate)) {\n            endField.setValue(startField.getValue());\n\n            if (startField.getOnDate() != null) {\n                endField.setOnDate(startField.getOnDate());\n            } else if (startField.getOnWeekNumber() != null) {\n                endField.setOnWeekNumber(startField.getOnWeekNumber());\n            } else if (startField.getMonthYear() != null) {\n                endField.setMonthYear(startField.getMonthYear());\n            } else if (startField.getAfterUnits() != null) {\n                endField.setAfterUnits(startField.getAfterUnits());\n            }\n        }\n    }\n\n    private void fixStartField() {\n        LocalDate mStartDate = getStartDate();\n        LocalDate mEndDate = getEndDate();\n\n        if (mStartDate == null || mEndDate == null) {\n            return;\n        }\n\n        if (mEndDate.isBefore(mStartDate)) {\n            startField.setValue(endField.getValue());\n\n            if (startField.getOnDate() != null) {\n                startField.setOnDate(endField.getOnDate());\n            } else if (endField.getOnWeekNumber() != null) {\n                startField.setOnWeekNumber(endField.getOnWeekNumber());\n            } else if (endField.getMonthYear() != null) {\n                startField.setMonthYear(endField.getMonthYear());\n            } else if (endField.getAfterUnits() != null) {\n                startField.setAfterUnits(endField.getAfterUnits());\n            }\n        }\n    }\n\n    private final ReadOnlyIntegerWrapper unitsToPrint = new ReadOnlyIntegerWrapper(this, \"unitsToPrint\");\n\n    public final ReadOnlyIntegerProperty unitsToPrintProperty() {\n        return unitsToPrint.getReadOnlyProperty();\n    }\n\n    public final int getUnitsToPrint() {\n        return unitsToPrint.get();\n    }\n\n    private void setUnitsToPrint(int unitsToPrint) {\n        this.unitsToPrint.set(unitsToPrint);\n    }\n\n    private void updateUnitsToPrint() {\n        LocalDate mStartDate = getStartDate();\n        LocalDate mEndDate = getEndDate();\n\n        if (mStartDate == null || mEndDate == null) {\n            return;\n        }\n\n        Duration duration = Duration.between(mStartDate.atTime(LocalTime.MIN), mEndDate.atStartOfDay().plusDays(1));\n        long days = duration.toDays();\n\n        if (days > 0) {\n            switch (getViewType()) {\n                case DAY_VIEW:\n                    setUnitsToPrint((int) days);\n                    break;\n\n                case WEEK_VIEW:\n                    setUnitsToPrint((int) Math.round((double) days / 7));\n                    break;\n\n                case MONTH_VIEW:\n                    setUnitsToPrint((int) Math.round((double) days / 30));\n                    break;\n\n                default:\n                    throw new UnsupportedOperationException(\"Not supported yet!: \" + getViewType());\n            }\n        } else {\n            setUnitsToPrint(0);\n        }\n    }\n\n    public void cleanOldValues() {\n        oldValuesMap.replace(ViewType.DAY_VIEW, null);\n        oldValuesMap.replace(ViewType.WEEK_VIEW, null);\n        oldValuesMap.replace(ViewType.MONTH_VIEW, null);\n        startDate.removeListener(OldValuesListener);\n        endDate.removeListener(OldValuesListener);\n    }\n\n    private void catchOldValues() {\n\n        TimeRangeOldValues value = oldValuesMap.get(getViewType());\n        if (value == null) {\n            value = new TimeRangeOldValues();\n        }\n\n        value.startField.setValue(startField.getValue());\n        value.startField.setOnDate(startField.getOnDate());\n        value.startField.setOnWeekNumber(startField.getOnWeekNumber());\n        value.startField.setMonthYear(startField.getMonthYear());\n\n        value.endField.setValue(endField.getValue());\n        value.endField.setOnDate(endField.getOnDate());\n        value.endField.setOnWeekNumber(endField.getOnWeekNumber());\n        value.endField.setMonthYear(endField.getMonthYear());\n        value.endField.setAfterUnits(endField.getAfterUnits());\n\n        oldValuesMap.replace(getViewType(), value);\n    }\n\n    private class TimeRangeOldValues {\n\n        private final TimeRangeField startField;\n        private final TimeRangeField endField;\n\n        public TimeRangeOldValues() {\n            startField = new TimeRangeField();\n            endField = new TimeRangeField();\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/ViewType.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.time.temporal.ChronoUnit;\n\nimport javafx.print.PageOrientation;\n\n/**\n * An enumerator listing the different views that are supported by the print\n * preview functionality.\n */\npublic enum ViewType {\n\n    DAY_VIEW {\n        @Override\n        public String getMessageKey() {\n            return \"PrintViewType.DAY_VIEW\";\n        }\n\n        @Override\n        public String getSingularChronoMessageKey() {\n            return \"PrintViewType.DAY_SINGULAR_CHRONO\";\n        }\n\n        @Override\n        public String getPluralChronoMessageKey() {\n            return \"PrintViewType.DAY_PLURAL_CHRONO\";\n        }\n\n        @Override\n        public PageOrientation getPageOrientation() {\n            return PageOrientation.PORTRAIT;\n        }\n\n        @Override\n        public DateTimeFormatter getDateTimeFormatter() {\n            return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);\n        }\n\n        @Override\n        public ChronoUnit getChronoUnit() {\n            return ChronoUnit.DAYS;\n        }\n\n    },\n\n    WEEK_VIEW {\n        @Override\n        public String getMessageKey() {\n            return \"PrintViewType.WEEK_VIEW\";\n        }\n\n        @Override\n        public String getSingularChronoMessageKey() {\n            return \"PrintViewType.WEEK_SINGULAR_CHRONO\";\n        }\n\n        @Override\n        public String getPluralChronoMessageKey() {\n            return \"PrintViewType.WEEK_PLURAL_CHRONO\";\n        }\n\n        @Override\n        public PageOrientation getPageOrientation() {\n            return PageOrientation.LANDSCAPE;\n        }\n\n        @Override\n        public DateTimeFormatter getDateTimeFormatter() {\n            return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);\n        }\n\n        @Override\n        public ChronoUnit getChronoUnit() {\n            return ChronoUnit.WEEKS;\n        }\n    },\n\n    MONTH_VIEW {\n        @Override\n        public String getMessageKey() {\n            return \"PrintViewType.MONTH_VIEW\";\n        }\n\n        @Override\n        public String getSingularChronoMessageKey() {\n            return \"PrintViewType.MONTH_SINGULAR_CHRONO\";\n        }\n\n        @Override\n        public String getPluralChronoMessageKey() {\n            return \"PrintViewType.MONTH_PLURAL_CHRONO\";\n        }\n\n        @Override\n        public PageOrientation getPageOrientation() {\n            return PageOrientation.LANDSCAPE;\n        }\n\n        @Override\n        public DateTimeFormatter getDateTimeFormatter() {\n            return DateTimeFormatter.ofPattern(\"MMMM yyyy\");\n        }\n\n        @Override\n        public ChronoUnit getChronoUnit() {\n            return ChronoUnit.MONTHS;\n        }\n    };\n\n    public abstract String getMessageKey();\n\n    public abstract String getSingularChronoMessageKey();\n\n    public abstract String getPluralChronoMessageKey();\n\n    public abstract PageOrientation getPageOrientation();\n\n    public abstract DateTimeFormatter getDateTimeFormatter();\n\n    public abstract ChronoUnit getChronoUnit();\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/ViewTypeControl.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.view.CalendarFXControl;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport org.controlsfx.control.PropertySheet;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * The abstract superclass of all controls inside the print package that are\n * supporting the different view types listed inside the {@link ViewType}\n * enumerator.\n */\npublic abstract class ViewTypeControl extends CalendarFXControl {\n\n    // view type support\n\n    private final ObjectProperty<ViewType> viewType = new SimpleObjectProperty<ViewType>(this, \"viewType\", ViewType.DAY_VIEW) {\n        @Override\n        public void set(ViewType newValue) {\n            super.set(Objects.requireNonNull(newValue));\n        }\n    };\n\n    /**\n     * A property used to store the currently active view type (e.g. \"day\").\n     *\n     * @return the current view type\n     */\n    public final ObjectProperty<ViewType> viewTypeProperty() {\n        return viewType;\n    }\n\n    /**\n     * Returns the value of {@link #viewTypeProperty()}.\n     *\n     * @return the current view type\n     */\n    public final ViewType getViewType() {\n        return viewTypeProperty().get();\n    }\n\n    /**\n     * Sets the value of {@link #viewTypeProperty()}.\n     *\n     * @param type the new view type\n     */\n    public final void setViewType(ViewType type) {\n        viewTypeProperty().set(type);\n    }\n\n    private static final String VIEW_TYPE_CONTROL = \"View Type\";\n\n    public ObservableList<PropertySheet.Item> getPropertySheetItems() {\n        ObservableList<PropertySheet.Item> items = super.getPropertySheetItems();\n\n        items.add(new PropertySheet.Item() {\n            @Override\n            public void setValue(Object value) {\n                setViewType((ViewType) value);\n            }\n\n            @Override\n            public Object getValue() {\n                return getViewType();\n            }\n\n            @Override\n            public Class<?> getType() {\n                return ViewType.class;\n            }\n\n            @Override\n            public Optional<ObservableValue<?>> getObservableValue() {\n                return Optional.of(viewTypeProperty());\n            }\n\n            @Override\n            public String getName() {\n                return \"View Type\";\n            }\n\n            @Override\n            public String getDescription() {\n                return \"Print View Type\";\n            }\n\n            @Override\n            public String getCategory() {\n                return VIEW_TYPE_CONTROL;\n            }\n        });\n\n        return items;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/ZoomPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view.print;\n\nimport com.calendarfx.view.CalendarFXControl;\nimport impl.com.calendarfx.view.print.ZoomPaneSkin;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.Region;\n\nimport java.util.Objects;\n\n/**\n * The zoom pane manages a zoom property, which is used to scale the pane's\n * content.\n */\npublic class ZoomPane extends CalendarFXControl {\n\n    public static final double MIN_ZOOM_VALUE = 1.0;\n\n    public static final double MAX_ZOOM_VALUE = 5.0;\n\n    public static final String DEFAULT_STYLE = \"zoom-pane\";\n\n    /**\n     * Constructs a new zoom pane.\n     */\n    public ZoomPane() {\n        super();\n        getStyleClass().add(DEFAULT_STYLE);\n\n        zoomProperty().addListener(obs -> {\n            if (getZoom() < MIN_ZOOM_VALUE || getZoom() > MAX_ZOOM_VALUE) {\n                throw new IndexOutOfBoundsException(\"The new value is out of bounds!\");\n            }\n        });\n\n        setPrefSize(700, 700);\n    }\n\n    /**\n     * Constructs a new zoom pane.\n     *\n     * @param content the initial content\n     */\n    public ZoomPane(Region content) {\n        this();\n        setContent(content);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new ZoomPaneSkin(this);\n    }\n\n    // content support\n\n    private final ObjectProperty<Region> content = new SimpleObjectProperty<Region>(this, \"content\") {\n        @Override\n        public void set(Region newValue) {\n            Objects.requireNonNull(newValue);\n            setZoom(MIN_ZOOM_VALUE);\n            super.set(newValue);\n        }\n    };\n\n    public final ObjectProperty<Region> contentProperty() {\n        return content;\n    }\n\n    public final Region getContent() {\n        return contentProperty().get();\n    }\n\n    public final void setContent(Region content) {\n        contentProperty().set(content);\n    }\n\n    // zoom support\n\n    private final DoubleProperty zoom = new SimpleDoubleProperty(this, \"zoom\", MIN_ZOOM_VALUE);\n\n    public final DoubleProperty zoomProperty() {\n        return zoom;\n    }\n\n    public final double getZoom() {\n        return zoomProperty().get();\n    }\n\n    public final void setZoom(double zoom) {\n        zoomProperty().set(zoom);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/com/calendarfx/view/print/package-info.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * A set of controls used for creating a print preview dialog.\n *\n * <img width=\"100%\" src=\"doc-files/preview-dialog.png\" alt=\"Preview Dialog\">\n */\npackage com.calendarfx.view.print;\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/AgendaViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.AgendaView.AgendaEntry;\nimport com.calendarfx.view.Messages;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.ListChangeListener;\nimport javafx.scene.control.Control;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListView;\nimport javafx.scene.layout.BorderPane;\n\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Level;\n\npublic class AgendaViewSkin extends DateControlSkin<AgendaView> implements LoadDataSettingsProvider {\n\n    private static final String AGENDA_VIEW_PLACEHOLDER_LABEL = \"placeholder-label\";\n\n    private final ListView<AgendaEntry> listView;\n\n    private final DataLoader dataLoader = new DataLoader(this);\n\n    private final Label statusLabel;\n\n    public AgendaViewSkin(AgendaView view) {\n        super(view);\n\n        listView = view.getListView();\n        listView.setMinWidth(1);\n        listView.setFixedCellSize(-1);\n        listView.setSelectionModel(Util.createEmptySelectionModel());\n        listView.getStyleClass().add(\"agenda-view-list\");\n\n        statusLabel = new Label();\n        statusLabel.getStyleClass().add(\"status-label\");\n        statusLabel.setMaxWidth(Double.MAX_VALUE);\n        statusLabel.visibleProperty().bind(view.showStatusLabelProperty());\n        statusLabel.managedProperty().bind(statusLabel.visibleProperty());\n\n        Label placeholderLabel = new Label(Messages.getString(\"AgendaViewSkin.NO_ENTRIES\"));\n        placeholderLabel.getStyleClass().add(AGENDA_VIEW_PLACEHOLDER_LABEL);\n        listView.setPlaceholder(placeholderLabel);\n\n        BorderPane borderPane = new BorderPane();\n        borderPane.getStyleClass().add(\"container\");\n        getChildren().add(borderPane);\n\n        borderPane.setCenter(listView);\n        borderPane.setTop(statusLabel);\n\n        ChangeListener reloadListener = (obs, oldValue, newValue) -> updateList(\"a view property has changed, property = \" + obs.toString());\n        view.lookAheadPeriodInDaysProperty().addListener(reloadListener);\n        view.lookBackPeriodInDaysProperty().addListener(reloadListener);\n        view.enableHyperlinksProperty().addListener(reloadListener);\n        view.dateProperty().addListener(reloadListener);\n\n        ListChangeListener<? super Calendar> calendarListListener = change -> {\n            updateList(\"the calendar list has changed\");\n            listenToCalendars();\n        };\n\n        view.getCalendars().addListener(calendarListListener);\n\n        updateList(\"initial loading\");\n        listenToCalendars();\n    }\n\n    private final InvalidationListener calendarVisibilityChanged = it -> updateList(\"calendar visibility changed\");\n\n    private final WeakInvalidationListener weakCalendarVisibilityChanged = new WeakInvalidationListener(calendarVisibilityChanged);\n\n    private void listenToCalendars() {\n        for (Calendar c : getSkinnable().getCalendars()) {\n            getSkinnable().getCalendarVisibilityProperty(c).addListener(weakCalendarVisibilityChanged);\n        }\n    }\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        updateList(\"calendar changed\");\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        updateList(evt, \"entry interval changed, entry = \" + evt.getEntry());\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        updateList(evt, \"entry recurrence rule changed, entry = \" + evt.getEntry());\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n        updateList(evt, \"entry full day changed changed, entry = \" + evt.getEntry());\n    }\n\n    @Override\n    protected void entryTitleChanged(CalendarEvent evt) {\n        updateList(evt, \"entry title changed changed, entry = \" + evt.getEntry());\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        updateList(evt, \"entry calendar changed, entry = \" + evt.getEntry());\n    }\n\n    @Override\n    protected void refreshData() {\n        updateList(\"data refresh\");\n    }\n\n    private void updateList(final CalendarEvent evt, String reason) {\n        Entry<?> entry = evt.getEntry();\n\n        // TODO: this can be optimized more to only update when really needed\n        if (isRelevant(entry)) {\n            updateList(reason);\n        }\n    }\n\n    private void updateList(String reason) {\n        if (LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"updating list inside agenda view, reason = \" + reason);\n        }\n\n        Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n        dataLoader.loadEntries(dataMap);\n\n        List<AgendaEntry> listEntries = new ArrayList<>();\n        for (LocalDate date : dataMap.keySet()) {\n            AgendaEntry listViewEntry = new AgendaEntry(date);\n            for (Entry<?> entry : dataMap.get(date)) {\n                if (!entry.isHidden()) {\n                    listViewEntry.getEntries().add(entry);\n                }\n            }\n            if (!listViewEntry.getEntries().isEmpty()) {\n                listEntries.add(listViewEntry);\n            }\n        }\n\n        Collections.sort(listEntries);\n        listView.getItems().setAll(listEntries);\n\n        String startTime = getSkinnable().getDateTimeFormatter().format(getLoadStartDate());\n        String endTime = getSkinnable().getDateTimeFormatter().format(getLoadEndDate());\n\n        statusLabel.setText(MessageFormat.format(Messages.getString(\"AgendaViewSkin.AGENDA_TIME_RANGE\"), startTime, endTime));\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"Agenda View\";\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        return getSkinnable().getDate().minusDays(getSkinnable().getLookBackPeriodInDays());\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        return getSkinnable().getDate().plusDays(getSkinnable().getLookAheadPeriodInDays());\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        return getSkinnable().getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayEntryViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.AllDayEntryView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\n\n@SuppressWarnings(\"javadoc\")\npublic class AllDayEntryViewSkin extends SkinBase<AllDayEntryView> {\n\n    protected Label titleLabel;\n\n    public AllDayEntryViewSkin(AllDayEntryView view) {\n        super(view);\n\n        titleLabel = new Label();\n        titleLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        titleLabel.setMouseTransparent(true);\n        titleLabel.setManaged(false);\n\n        Entry<?> entry = view.getEntry();\n\n        entry.calendarProperty().addListener(weakUpdateViewListener);\n        entry.fullDayProperty().addListener(weakUpdateViewListener);\n        entry.titleProperty().addListener(weakUpdateViewListener);\n        entry.intervalProperty().addListener(weakUpdateViewListener);\n\n        updateView();\n\n        getChildren().addAll(titleLabel);\n    }\n\n    private final InvalidationListener updateViewListener = it -> updateView();\n\n    private final WeakInvalidationListener weakUpdateViewListener = new WeakInvalidationListener(updateViewListener);\n\n    protected void updateView() {\n        final AllDayEntryView view = getSkinnable();\n\n        Entry<?> entry = view.getEntry();\n        Calendar calendar = entry.getCalendar();\n\n        if (calendar == null) {\n            return;\n        }\n\n        view.getStyleClass().setAll(\n                \"default-style-entry-small\",\n                calendar.getStyle() + \"-entry-small\",\n                \"default-style-entry-small-full-day\",\n                calendar.getStyle() + \"-entry-small-full-day\");\n\n        // title style\n        titleLabel.getStyleClass().setAll(\n                \"default-style-entry-small-title-label\",\n                calendar.getStyle() + \"-entry-small-title-label\",\n                \"default-style-entry-small-title-label-full-day\",\n                calendar.getStyle() + \"-entry-small-title-label-full-day\");\n\n        titleLabel.setText(entry.getTitle());\n\n        view.getStyleClass().add(\"default-style-entry-small-only\");\n        view.getStyleClass().addAll(entry.getStyleClass());\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        titleLabel.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY), snapSizeX(contentWidth), snapSizeY(contentHeight));\n    }\n\n    @Override\n    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return titleLabel.prefHeight(-1) + topInset + bottomInset;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.AllDayEntryView;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.DraggedEntry;\nimport com.calendarfx.view.EntryViewBase;\nimport impl.com.calendarfx.view.util.Placement;\nimport impl.com.calendarfx.view.util.TimeBoundsResolver;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.geometry.Insets;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.control.Control;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\n\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.stream.Collectors;\n\npublic class AllDayViewSkin extends DateControlSkin<AllDayView> implements LoadDataSettingsProvider {\n\n    private static final String ALL_DAY_BACKGROUND_REGION = \"day-region\";\n    private static final String ALL_DAY_BACKGROUND_REGION_TODAY = \"today\";\n    private static final String ALL_DAY_BACKGROUND_REGION_WEEKEND = \"weekend\";\n\n    private final DataLoader dataLoader;\n    private final HBox container;\n\n    private final Group entryViewGroup = new Group();\n\n    public AllDayViewSkin(AllDayView view) {\n        super(view);\n\n        view.setFocusTraversable(true);\n\n        container = new HBox();\n        container.getStyleClass().add(\"container\");\n        getChildren().add(container);\n\n        entryViewGroup.setMouseTransparent(false);\n        entryViewGroup.setManaged(false);\n        getChildren().add(entryViewGroup);\n\n        // update backgrounds\n        InvalidationListener updateBackgroundsListener = evt -> updateBackgrounds();\n        view.numberOfDaysProperty().addListener(updateBackgroundsListener);\n        view.showTodayProperty().addListener(updateBackgroundsListener);\n        view.weekFieldsProperty().addListener(updateBackgroundsListener);\n        view.adjustToFirstDayOfWeekProperty().addListener(updateBackgroundsListener);\n\n        // update entries\n        InvalidationListener updateEntriesListener = evt -> updateEntries(\"a view property changed\");\n        view.numberOfDaysProperty().addListener(updateEntriesListener);\n        view.dateProperty().addListener(updateEntriesListener);\n        view.extraPaddingProperty().addListener(updateEntriesListener);\n        view.rowHeightProperty().addListener(updateEntriesListener);\n        view.rowSpacingProperty().addListener(updateEntriesListener);\n        view.columnSpacingProperty().addListener(updateEntriesListener);\n        view.weekFieldsProperty().addListener(updateEntriesListener);\n        view.adjustToFirstDayOfWeekProperty().addListener(updateEntriesListener);\n\n        updateBackgrounds();\n\n        dataLoader = new DataLoader(this);\n\n        updateEntries(\"initial load\");\n    }\n\n    @Override\n    protected void refreshData() {\n        updateEntries(\"refreshData() called\");\n    }\n\n    private void updateEntries(String reason) {\n        LoggingDomain.PERFORMANCE.fine(\"updating entries, reason: \" + reason);\n\n        entryViewGroup.getChildren().clear();\n\n        Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n        dataLoader.loadEntries(dataMap);\n\n        Set<Entry<?>> entrySet = new HashSet<>();\n        for (List<Entry<?>> entryList : dataMap.values()) {\n            entrySet.addAll(entryList);\n        }\n\n        entrySet.removeIf(ref -> !ref.isFullDay());\n\n        List<Entry<?>> entryList = new ArrayList<>(entrySet);\n\n        for (Entry<?> entry : entryList) {\n            doAddEntryView(entry);\n        }\n\n        getSkinnable().autosize();\n\n        getSkinnable().requestLayout();\n    }\n\n    private List<EntryViewBase> findEntryViews(Entry<?> entry) {\n        return entryViewGroup.getChildren().stream()\n                .map(node -> (EntryViewBase) node)\n                .filter(e -> e.getEntry().getId().equals(entry.getId()))\n                .collect(Collectors.toList());\n    }\n\n    private boolean removeEntryViews(Entry<?> entry, String reason) {\n        if (reason != null) {\n            LoggingDomain.VIEW.fine(\"removing entry, reason = \" + reason + \", date = \" + getSkinnable().getDate());\n        }\n\n        boolean removed = Util.removeChildren(entryViewGroup, node -> {\n            AllDayEntryView view = (AllDayEntryView) node;\n            Entry<?> viewEntry = view.getEntry();\n            return viewEntry.getId().equals(entry.getId());\n        });\n\n        if (removed && !(entry instanceof DraggedEntry) && LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"successfully removed the entry view of entry \" + entry);\n        }\n\n        return removed;\n    }\n\n    private void addEntryViews(Entry<?> entry, String reason) {\n        LoggingDomain.VIEW.fine(\"adding entry, reason = \" + reason + \", date = \" + getSkinnable().getDate());\n        if (entry.isRecurring()) {\n            Map<LocalDate, Entry<?>> recurrenceEntries = findRecurrenceEntries(entry);\n            recurrenceEntries.forEach((date, recurrence) -> doAddEntryView(recurrence));\n        } else {\n            doAddEntryView(entry);\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    private Map<LocalDate, Entry<?>> findRecurrenceEntries(Entry<?> entry) {\n        Calendar calendar = entry.getCalendar();\n        LocalDate startDate = getLoadStartDate();\n        LocalDate endDate = getLoadEndDate();\n\n        Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(startDate, endDate, getZoneId());\n        Map<LocalDate, Entry<?>> result = new HashMap<>();\n\n        entries.forEach((date, list) -> {\n            if (!list.isEmpty()) {\n                Optional<Entry<?>> first = list.stream()\n                        .filter(e -> e.getId().equals(entry.getId()))\n                        .filter(e -> e.getStartDate().equals(date))\n                        .findFirst();\n                if (first.isPresent()) {\n                    result.put(date, first.get());\n                }\n            }\n        });\n\n        return result;\n    }\n\n    private AllDayEntryView doAddEntryView(Entry<?> entry) {\n        Callback<Entry<?>, AllDayEntryView> factory = getSkinnable().getEntryViewFactory();\n        AllDayEntryView view = factory.call(entry);\n        view.getProperties().put(\"control\", getSkinnable());\n        view.setManaged(false);\n\n        int index = findIndex(entry);\n        entryViewGroup.getChildren().add(index, view);\n\n        if (!(entry instanceof DraggedEntry) && LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"added entry view \" + entry.getTitle() + \", day = \" + getSkinnable().getDate());\n        }\n\n        return view;\n    }\n\n    /*\n     * Utility method to find the right place for inserting a new day entry\n     * view. The right order is important for TAB traversal to work properly.\n     */\n    private int findIndex(Entry<?> entry) {\n        int childrenSize = entryViewGroup.getChildren().size();\n\n        for (int i = 0; i < childrenSize; i++) {\n            Node node = entryViewGroup.getChildren().get(i);\n            AllDayEntryView view = (AllDayEntryView) node;\n            Entry<?> viewEntry = view.getEntry();\n            if (viewEntry.getStartAsZonedDateTime().isAfter(entry.getStartAsZonedDateTime())) {\n                return i;\n            }\n        }\n\n        return childrenSize;\n    }\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        updateEntries(\"calendar changed\");\n    }\n\n    @Override\n    protected void entryTitleChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry title changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n        if (entry.isFullDay()) {\n            // no need to check for relevance, probably faster to just look for entry views\n            findEntryViews(entry).forEach(entryView -> entryView.getEntry().setTitle(evt.getEntry().getTitle()));\n        }\n    }\n\n    @Override\n    protected void entryLocationChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry location changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n        if (entry.isFullDay()) {\n            // no need to check for relevance, probably faster to just look for entry views\n            findEntryViews(entry).forEach(entryView -> entryView.getEntry().setLocation(evt.getEntry().getLocation()));\n        }\n    }\n\n    @Override\n    protected void entryUserObjectChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry user object changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n        if (entry.isFullDay()) {\n            // no need to check for relevance, probably faster to just look for entry views\n            findEntryViews(entry).forEach(entryView -> entryView.getEntry().setUserObject(evt.getEntry().getUserObject()));\n        }\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry calendar changed, date = \" + getSkinnable().getDate());\n\n        Entry<?> entry = evt.getEntry();\n        if (evt.getCalendar() == null) {\n            removeEntryViews(entry, \"entry was deleted\");\n        } else {\n            if (entry.isFullDay() && isRelevant(entry)) {\n                List<EntryViewBase> entryView = findEntryViews(entry);\n                if (!entryView.isEmpty()) {\n                    entryView.forEach(view -> view.getEntry().setCalendar(evt.getCalendar()));\n                } else {\n                    addEntryViews(entry, \"entry calendar changed\");\n                }\n            }\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry full day flag changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n        if (isRelevant(entry)) {\n            removeEntryViews(entry, \"full day flag changed to false\");\n            if (entry.isFullDay()) {\n                addEntryViews(entry, \"full day flag changed to true, no entry view can be present\");\n            }\n        }\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry recurrence rule changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n\n        /*\n         * We only care about full day entries in this view.\n         */\n        if (entry.isFullDay()) {\n            // remove all entry views\n            removeEntryViews(entry, \"recurrence rule changed\");\n            if (isRelevant(entry)) {\n                addEntryViews(entry, \"recurrence rule changed\");\n            }\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry interval changed, date = \" + getSkinnable().getDate());\n\n        Entry<?> entry = evt.getEntry();\n\n        /*\n         * We only care about full day entries in this view.\n         */\n        if (entry.isFullDay()) {\n            // remove all entry views\n            removeEntryViews(entry, \"interval changed\");\n            if (isRelevant(entry)) {\n                addEntryViews(entry, \"interval changed\");\n            }\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n\n        List<AllDayEntryView> entryViews = entryViewGroup.getChildren().stream().filter(node -> node instanceof AllDayEntryView).map(node -> (AllDayEntryView) node).collect(Collectors.toList());\n\n        List<Placement> placements = TimeBoundsResolver.resolve(entryViews);\n\n        int maxPosition = 0;\n        for (Placement p : placements) {\n            maxPosition = Math.max(maxPosition, p.getColumnIndex());\n        }\n\n        AllDayView view = getSkinnable();\n\n        Insets insets = view.getInsets();\n        Insets extraPadding = view.getExtraPadding();\n\n        double rowHeight = view.getRowHeight();\n        double rowSpacing = view.getRowSpacing();\n        return (maxPosition + 1) * rowHeight + (maxPosition * rowSpacing) + insets.getTop() + insets.getBottom() * extraPadding.getTop() + extraPadding.getBottom();\n    }\n\n    @Override\n    protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        super.layoutChildren(contentX, contentY, contentWidth, contentHeight);\n\n        AllDayView view = getSkinnable();\n\n        double rowHeight = view.getRowHeight();\n        double rowSpacing = view.getRowSpacing();\n\n        double height = 0;\n\n        Insets extraPadding = view.getExtraPadding();\n\n        List<AllDayEntryView> entryViews = entryViewGroup.getChildren().stream().map(node -> (AllDayEntryView) node).collect(Collectors.toList());\n\n        List<Placement> placements = TimeBoundsResolver.resolve(entryViews);\n\n        for (Placement placement : placements) {\n            EntryViewBase<?> entryView = placement.getEntryView();\n            Entry<?> entry = entryView.getEntry();\n\n            LocalDate startDate = view.getDate();\n            if (view.isAdjustToFirstDayOfWeek()) {\n                startDate = Util.adjustToFirstDayOfWeek(view.getDate(), view.getFirstDayOfWeek());\n            }\n\n            LocalDate endDate = startDate.plusDays(view.getNumberOfDays() - 1);\n\n            long deltaDays = ChronoUnit.DAYS.between(startDate, entry.getStartDate());\n\n            long entryDurationInDays = ChronoUnit.DAYS.between(entry.getStartDate(), entry.getEndDate()) + 1;\n\n            if (deltaDays < 0) {\n                entryDurationInDays += deltaDays;\n            }\n\n            if (entry.getStartDate().isBefore(startDate)) {\n                entryView.getProperties().put(\"startDate\", startDate);\n            } else {\n                entryView.getProperties().put(\"startDate\", entry.getStartDate());\n            }\n\n            if (entry.getEndDate().isAfter(endDate)) {\n                entryView.getProperties().put(\"endDate\", endDate);\n            } else {\n                entryView.getProperties().put(\"endDate\", entry.getEndDate());\n            }\n\n            entryDurationInDays = Math.max(entryDurationInDays, 1);\n\n            double dayWidth = contentWidth / view.getNumberOfDays();\n\n            double x = Math.max(0, contentX + (deltaDays * dayWidth));\n            double y = contentY + placement.getColumnIndex() * (rowHeight + rowSpacing) + extraPadding.getTop();\n\n            double w;\n            if (view.getNumberOfDays() == 1) {\n                w = contentWidth + 1;\n            } else {\n                w = Math.min(entryDurationInDays * dayWidth - view.getColumnSpacing(), contentWidth - x);\n            }\n\n            entryView.setMaxHeight(rowHeight);\n\n            entryView.resizeRelocate(snapPositionX(x), snapPositionY(y), snapSizeX(w), snapSizeY(rowHeight));\n\n            height = Math.max(height, y + rowHeight);\n        }\n    }\n\n    private void updateBackgrounds() {\n        container.getChildren().clear();\n\n        AllDayView allDayView = getSkinnable();\n        Callback<AllDayView, Region> separatorFactory = allDayView.getSeparatorFactory();\n\n        int numberOfDays = allDayView.getNumberOfDays();\n        for (int i = 0; i < numberOfDays; i++) {\n            Region region = new Region();\n            region.setPrefWidth(1); // equal width distribution\n            region.setMaxWidth(Double.MAX_VALUE);\n            region.getStyleClass().add(ALL_DAY_BACKGROUND_REGION);\n\n            final int day = i;\n            allDayView.dateProperty().addListener(evt -> updateRegion(region, day));\n            updateRegion(region, day);\n\n            HBox.setHgrow(region, Priority.ALWAYS);\n            container.getChildren().add(region);\n\n            if (separatorFactory != null && i < numberOfDays - 1) {\n                Region separator = separatorFactory.call(allDayView);\n                if (separator != null) {\n                    container.getChildren().add(separator);\n                    HBox.setHgrow(separator, Priority.NEVER);\n                }\n            }\n        }\n\n        allDayView.requestLayout();\n    }\n\n    private void updateRegion(Region region, int day) {\n        final AllDayView view = getSkinnable();\n\n        LocalDate startDate = view.getDate();\n\n        if (view.isAdjustToFirstDayOfWeek()) {\n            startDate = Util.adjustToFirstDayOfWeek(view.getDate(), view.getFirstDayOfWeek());\n        }\n\n        LocalDate date = getDate(startDate, day);\n\n        if (view.isShowToday() && date.equals(view.getToday())) {\n            if (!region.getStyleClass().contains(ALL_DAY_BACKGROUND_REGION_TODAY)) {\n                region.getStyleClass().add(ALL_DAY_BACKGROUND_REGION_TODAY);\n            }\n        } else {\n            region.getStyleClass().remove(ALL_DAY_BACKGROUND_REGION_TODAY);\n        }\n\n        if (view.getWeekendDays().contains(date.getDayOfWeek())) {\n            if (!region.getStyleClass().contains(ALL_DAY_BACKGROUND_REGION_WEEKEND)) {\n                region.getStyleClass().add(ALL_DAY_BACKGROUND_REGION_WEEKEND);\n            }\n        } else {\n            region.getStyleClass().remove(ALL_DAY_BACKGROUND_REGION_WEEKEND);\n        }\n    }\n\n    private LocalDate getDate(LocalDate startDate, int dayCount) {\n        return startDate.plusDays(dayCount);\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"All Day View\";\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        AllDayView view = getSkinnable();\n\n        if (view.isAdjustToFirstDayOfWeek()) {\n\n            /*\n             * The month view also shows the last couple of days of the previous\n             * month.\n             */\n            return Util.adjustToFirstDayOfWeek(view.getDate(), view.getFirstDayOfWeek());\n\n        }\n\n        return view.getDate();\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        return getLoadStartDate().plusDays(getSkinnable().getNumberOfDays() - 1);\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        return getSkinnable().getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/AutoScrollPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.DayEntryView;\nimport javafx.application.Platform;\nimport javafx.event.EventTarget;\nimport javafx.scene.Node;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.scene.input.DragEvent;\nimport javafx.scene.input.Dragboard;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.input.TransferMode;\n\n/**\n * A specialized scrollpane used for automatic scrolling when the user performs\n * a drag operation close to the edges of the pane.\n */\npublic class AutoScrollPane extends ScrollPane {\n\n    final double proximity = 20;\n\n    /**\n     * Constructs a new scrollpane.\n     */\n    public AutoScrollPane() {\n        this(null);\n    }\n\n    /**\n     * Constructs a new scrollpane for the given content node.\n     *\n     * @param content the content node\n     */\n    public AutoScrollPane(Node content) {\n        super(content);\n\n        // regular drag, e.g. of an entry view\n        addEventFilter(MouseEvent.MOUSE_DRAGGED, this::autoscrollIfNeeded);\n        addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> stopAutoScrollIfNeeded());\n\n        // drag and drop from the outside\n        addEventFilter(MouseEvent.DRAG_DETECTED, this::startDrag);\n        addEventFilter(DragEvent.DRAG_OVER, this::autoscrollIfNeeded);\n        addEventFilter(DragEvent.DRAG_EXITED, evt -> stopAutoScrollIfNeeded());\n        addEventFilter(DragEvent.DRAG_DROPPED, evt -> stopAutoScrollIfNeeded());\n        addEventFilter(DragEvent.DRAG_DONE, evt -> stopAutoScrollIfNeeded());\n    }\n\n    private void startDrag(MouseEvent evt) {\n        EventTarget target = evt.getTarget();\n        if (isScrollBar(target) || !isOnEntry(target)) {\n            return;\n        }\n        Dragboard db = startDragAndDrop(TransferMode.MOVE);\n        ClipboardContent content = new ClipboardContent();\n\n        /*\n         * We have to add some content, otherwise drag over will not be called.\n         */\n        content.putString(\"dummy\");\n        db.setContent(content);\n    }\n\n    private boolean isOnEntry(EventTarget target) {\n        if (target == null || !(target instanceof Node)) {\n            return false;\n        }\n\n        Node node = (Node) target;\n        if (node instanceof DayEntryView) {\n            return true;\n        }\n\n        return isOnEntry(node.getParent());\n    }\n\n    private boolean isScrollBar(EventTarget target) {\n        if (target instanceof Node) {\n            return isScrollBar((Node) target);\n        }\n\n        return false;\n    }\n\n    private boolean isScrollBar(Node node) {\n        boolean result = false;\n        if (node instanceof ScrollBar) {\n            result = true;\n        } else if (node.getParent() != null) {\n            return isScrollBar(node.getParent());\n        }\n\n        return result;\n    }\n\n    private void autoscrollIfNeeded(DragEvent evt) {\n        evt.acceptTransferModes(TransferMode.ANY);\n\n        if (getBoundsInLocal().getWidth() < 1) {\n            if (getBoundsInLocal().getWidth() < 1) {\n                stopAutoScrollIfNeeded();\n                return;\n            }\n        }\n\n        double yOffset = 0;\n\n        // y offset\n\n        double delta = evt.getSceneY() - localToScene(0, 0).getY();\n        if (delta < proximity) {\n            yOffset = -(proximity - delta);\n        }\n\n        delta = localToScene(0, 0).getY() + getHeight() - evt.getSceneY();\n        if (delta < proximity) {\n            yOffset = proximity - delta;\n        }\n\n        if (yOffset != 0) {\n            autoscroll(yOffset);\n        } else {\n            stopAutoScrollIfNeeded();\n        }\n    }\n\n    private void autoscrollIfNeeded(MouseEvent evt) {\n        if (getBoundsInLocal().getWidth() < 1) {\n            if (getBoundsInLocal().getWidth() < 1) {\n                stopAutoScrollIfNeeded();\n                return;\n            }\n        }\n\n        double yOffset = 0;\n\n        // y offset\n\n        double delta = evt.getSceneY() - localToScene(0, 0).getY();\n        if (delta < 0) {\n            yOffset = Math.max(delta / 2, -10);\n        }\n\n        delta = localToScene(0, 0).getY() + getHeight() - evt.getSceneY();\n        if (delta < 0) {\n            yOffset = Math.min(-delta / 2, 10);\n        }\n\n        if (yOffset != 0) {\n            autoscroll(yOffset);\n        } else {\n            stopAutoScrollIfNeeded();\n        }\n    }\n\n    class ScrollThread extends Thread {\n        private boolean running = true;\n        private double yOffset;\n\n        public ScrollThread() {\n            super(\"Autoscrolling List View\");\n            setDaemon(true);\n        }\n\n        @Override\n        public void run() {\n\n            /*\n             * Some initial delay, especially useful when dragging something in\n             * from the outside.\n             */\n\n            try {\n                Thread.sleep(300);\n            } catch (InterruptedException e1) {\n                e1.printStackTrace();\n            }\n\n            while (running) {\n\n                Platform.runLater(this::scrollY);\n\n                try {\n                    sleep(15);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private void scrollY() {\n            double percent = yOffset / getHeight();\n            setVvalue(getVvalue() + percent);\n        }\n\n        public void stopRunning() {\n            this.running = false;\n        }\n\n        public void setDelta(double yOffset) {\n            this.yOffset = yOffset;\n        }\n    }\n\n    private ScrollThread scrollThread;\n\n    private void autoscroll(double yOffset) {\n        if (scrollThread == null) {\n            scrollThread = new ScrollThread();\n            scrollThread.start();\n        }\n\n        scrollThread.setDelta(yOffset);\n    }\n\n    private void stopAutoScrollIfNeeded() {\n        if (scrollThread != null) {\n            scrollThread.stopRunning();\n            scrollThread = null;\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ButtonBarSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.ButtonBar;\nimport javafx.beans.Observable;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.HBox;\n\npublic class ButtonBarSkin extends SkinBase<ButtonBar> {\n\n    private static final String ONLY_BUTTON = \"only-button\";\n    private static final String LEFT_PILL = \"left-pill\";\n    private static final String CENTER_PILL = \"center-pill\";\n    private static final String RIGHT_PILL = \"right-pill\";\n\n    private final HBox container = new HBox();\n\n    public ButtonBarSkin(ButtonBar control) {\n        super(control);\n\n        this.container.getStyleClass().add(\"container\");\n\n        this.container.setFillHeight(true);\n\n        this.getChildren().add(this.container);\n        this.updateButtons();\n        this.getButtons().addListener((Observable observable) -> updateButtons());\n    }\n\n    private ObservableList<Button> getButtons() {\n        return getSkinnable().getButtons();\n    }\n\n    private void updateButtons() {\n        ObservableList<Button> buttons = this.getButtons();\n        this.container.getChildren().clear();\n\n        for (int i = 0; i < this.getButtons().size(); ++i) {\n            Button button = buttons.get(i);\n            button.getStyleClass().removeAll(ONLY_BUTTON, LEFT_PILL, CENTER_PILL, RIGHT_PILL);\n            button.setMaxHeight(Double.MAX_VALUE);\n            this.container.getChildren().add(button);\n            if (i == buttons.size() - 1) {\n                if (i == 0) {\n                    button.getStyleClass().add(ONLY_BUTTON);\n                } else {\n                    button.getStyleClass().add(RIGHT_PILL);\n                }\n            } else if (i == 0) {\n                button.getStyleClass().add(LEFT_PILL);\n            } else {\n                button.getStyleClass().add(CENTER_PILL);\n            }\n        }\n\n    }\n\n    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return getSkinnable().prefWidth(height);\n    }\n\n    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return getSkinnable().prefHeight(width);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/CalendarHeaderViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.CalendarHeaderView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.ListChangeListener.Change;\nimport javafx.css.PseudoClass;\nimport javafx.scene.Node;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.util.Callback;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@SuppressWarnings(\"javadoc\")\npublic class CalendarHeaderViewSkin extends SkinBase<CalendarHeaderView> {\n\n    private final GridPane pane;\n\n    private final PseudoClass FIRST_COLUMN = PseudoClass.getPseudoClass(\"first\");\n    private final PseudoClass MIDDLE_COLUMN = PseudoClass.getPseudoClass(\"middle\");\n    private final PseudoClass LAST_COLUMN = PseudoClass.getPseudoClass(\"last\");\n\n    private final InvalidationListener calendarVisibilityListener = it -> updateColumns();\n\n    private final WeakInvalidationListener weakCalendarVisibilityListener = new WeakInvalidationListener(calendarVisibilityListener);\n\n    /**\n     * Constructs a new header view skin.\n     *\n     * @param view the control to skin\n     */\n    public CalendarHeaderViewSkin(CalendarHeaderView view) {\n        super(view);\n\n        view.setFocusTraversable(false);\n\n        pane = new GridPane();\n        getChildren().add(pane);\n\n        InvalidationListener updateBackgroundsListener = evt -> updateColumns();\n        view.numberOfDaysProperty().addListener(updateBackgroundsListener);\n\n        updateColumns();\n\n        for (Calendar calendar : getSkinnable().getCalendars()) {\n            view.getCalendarVisibilityProperty(calendar).addListener(weakCalendarVisibilityListener);\n        }\n\n        getSkinnable().getCalendars().addListener((Change<? extends Calendar> change) -> {\n            while (change.next()) {\n                if (change.wasAdded()) {\n                    for (Calendar calendar : change.getAddedSubList()) {\n                        view.getCalendarVisibilityProperty(calendar).addListener(weakCalendarVisibilityListener);\n                    }\n                } else if (change.wasRemoved()) {\n                    for (Calendar calendar : change.getRemoved()) {\n                        view.getCalendarVisibilityProperty(calendar).removeListener(weakCalendarVisibilityListener);\n                    }\n                }\n            }\n\n            updateColumns();\n        });\n\n        view.visibleProperty().addListener(weakCalendarVisibilityListener);\n    }\n\n    private void updateColumns() {\n        pane.getChildren().clear();\n\n        final CalendarHeaderView headerView = getSkinnable();\n        if (headerView.isVisible()) {\n            Callback<Calendar, Node> factory = headerView.getCellFactory();\n\n            List<ColumnConstraints> weekConstraints = new ArrayList<>();\n\n            int numberOfDays = headerView.getNumberOfDays();\n            for (int i = 0; i < numberOfDays; i++) {\n                ColumnConstraints con = new ColumnConstraints();\n                con.setPercentWidth((double) 100 / (double) numberOfDays);\n                weekConstraints.add(con);\n\n                GridPane dayGridPane = new GridPane();\n                dayGridPane.setMaxWidth(Double.MAX_VALUE);\n                GridPane.setHgrow(dayGridPane, Priority.ALWAYS);\n                GridPane.setVgrow(dayGridPane, Priority.ALWAYS);\n                GridPane.setFillHeight(dayGridPane, true);\n                GridPane.setFillWidth(dayGridPane, true);\n\n                List<ColumnConstraints> dayConstraints = new ArrayList<>();\n\n                /*\n                 * We only care about visible calendars.\n                 */\n                List<Calendar> calendars = headerView.getCalendars().stream()\n                        .filter(headerView::isCalendarVisible)\n                        .collect(Collectors.toList());\n\n                int calendarCount = calendars.size();\n\n                double columnWidth = (double) 100 / (double) calendarCount;\n\n                for (int j = 0; j < calendarCount; j++) {\n                    Calendar calendar = calendars.get(j);\n\n                    ColumnConstraints calendarConstraint = new ColumnConstraints();\n                    calendarConstraint.setPercentWidth(columnWidth);\n                    calendarConstraint.setFillWidth(true);\n                    calendarConstraint.setHgrow(Priority.ALWAYS);\n                    dayConstraints.add(calendarConstraint);\n\n                    Node calendarLabel = factory.call(calendar);\n\n                    calendarLabel.getStyleClass().addAll(\"default-style-calendar-header\", calendar.getStyle() + \"-calendar-header\");\n                    dayGridPane.add(calendarLabel, j, 0);\n\n                    GridPane.setHgrow(calendarLabel, Priority.ALWAYS);\n                    GridPane.setVgrow(calendarLabel, Priority.ALWAYS);\n                    GridPane.setFillHeight(calendarLabel, true);\n                    GridPane.setFillWidth(calendarLabel, true);\n\n                    calendarLabel.pseudoClassStateChanged(FIRST_COLUMN, j == 0);\n                    calendarLabel.pseudoClassStateChanged(MIDDLE_COLUMN, j > 0 && j < calendarCount - 1);\n                    calendarLabel.pseudoClassStateChanged(LAST_COLUMN, j == calendarCount - 1);\n                }\n\n                dayGridPane.getColumnConstraints().setAll(dayConstraints);\n\n                pane.add(dayGridPane, i, 0);\n            }\n\n            pane.getColumnConstraints().setAll(weekConstraints);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/CalendarPropertySheet.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.TimeField;\nimport com.calendarfx.view.WeekFieldsView;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport org.controlsfx.control.PropertySheet;\nimport org.controlsfx.property.editor.AbstractPropertyEditor;\nimport org.controlsfx.property.editor.DefaultPropertyEditorFactory;\nimport org.controlsfx.property.editor.PropertyEditor;\n\nimport java.time.LocalTime;\nimport java.time.temporal.WeekFields;\n\npublic class CalendarPropertySheet extends PropertySheet {\n\n    public CalendarPropertySheet() {\n        this(FXCollections.emptyObservableList());\n    }\n\n    public CalendarPropertySheet(ObservableList<Item> items) {\n        super(items);\n        setPropertyEditorFactory(new Factory());\n    }\n\n    class Factory extends DefaultPropertyEditorFactory {\n\n        @Override\n        public PropertyEditor<?> call(Item property) {\n            PropertyEditor<?> editor = super.call(property);\n\n            if (editor == null) {\n                if (property.getType().equals(LocalTime.class)) {\n                    return new AbstractPropertyEditor<LocalTime, TimeField>(property, new TimeField()) {\n                        @Override\n                        protected ObservableValue<LocalTime> getObservableValue() {\n                            return getEditor().valueProperty();\n                        }\n\n                        @Override\n                        public void setValue(LocalTime value) {\n                            getEditor().setValue(value);\n                        }\n                    };\n                } else if (property.getType().equals(WeekFields.class)) {\n                    return new AbstractPropertyEditor<WeekFields, WeekFieldsView>(property, new WeekFieldsView()) {\n                        @Override\n                        protected ObservableValue<WeekFields> getObservableValue() {\n                            return getEditor().weekFieldsProperty();\n                        }\n\n                        @Override\n                        public void setValue(WeekFields value) {\n                            getEditor().setWeekFields(value);\n                        }\n                    };\n                }\n            }\n\n            return editor;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/CalendarSelectorSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.view.CalendarSelector;\nimport com.calendarfx.view.CalendarView;\nimport javafx.beans.Observable;\nimport javafx.scene.control.MenuButton;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.shape.Rectangle;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic class CalendarSelectorSkin extends SkinBase<CalendarSelector> {\n\n    private final MenuButton button;\n    private final Rectangle buttonIcon;\n\n    public CalendarSelectorSkin(CalendarSelector selector) {\n        super(selector);\n\n        buttonIcon = new Rectangle(12, 12);\n\n        button = new MenuButton();\n        button.setGraphic(buttonIcon);\n\n        getChildren().add(button);\n\n        selector.calendarProperty().addListener(it -> updateButton());\n\n        selector.getCalendars().addListener((Observable evt) -> updateMenuItems());\n\n        updateMenuItems();\n        updateButton();\n    }\n\n    private void updateButton() {\n        Calendar calendar = getSkinnable().getCalendar();\n        if (calendar != null) {\n            buttonIcon.getStyleClass().setAll(calendar.getStyle() + \"-icon\");\n        } else {\n            buttonIcon.getStyleClass().clear();\n        }\n    }\n\n    private void updateMenuItems() {\n        ToggleGroup group = new ToggleGroup();\n        List<MenuItem> items = new ArrayList<>();\n        for (Calendar calendar : getSkinnable().getCalendars()) {\n            RadioMenuItem item = new RadioMenuItem(calendar.getName());\n            Rectangle icon = new Rectangle(10, 10);\n            icon.setArcHeight(2);\n            icon.setArcWidth(2);\n            icon.getStyleClass().add(calendar.getStyle() + \"-icon\");\n            item.setGraphic(icon);\n            item.setDisable(calendar.isReadOnly());\n            item.setOnAction(evt -> getSkinnable().setCalendar(calendar));\n            group.getToggles().add(item);\n            items.add(item);\n            if (calendar.equals(getSkinnable().getCalendar())) {\n                item.setSelected(true);\n            }\n        }\n\n        button.getItems().setAll(items);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/CalendarViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.CalendarFXControl;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.CalendarView.Page;\nimport com.calendarfx.view.DeveloperConsole;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.RequestEvent;\nimport com.calendarfx.view.SearchResultView;\nimport com.calendarfx.view.SourceView;\nimport com.calendarfx.view.YearMonthView;\nimport com.calendarfx.view.page.PageBase;\nimport com.calendarfx.view.popover.ZoneIdComparator;\nimport com.calendarfx.view.print.PrintView;\nimport com.calendarfx.view.print.PrintablePage;\nimport com.calendarfx.view.print.ViewType;\nimport com.dlsc.gemsfx.SearchTextField;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.transformation.SortedList;\nimport javafx.geometry.HPos;\nimport javafx.geometry.Orientation;\nimport javafx.geometry.Pos;\nimport javafx.geometry.Side;\nimport javafx.geometry.VPos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ChoiceBox;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\nimport org.controlsfx.control.MasterDetailPane;\nimport org.controlsfx.control.SegmentedButton;\nimport org.controlsfx.control.textfield.CustomTextField;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\nimport java.time.ZoneId;\nimport java.util.function.Consumer;\n\nimport static com.calendarfx.view.CalendarView.Page.DAY;\nimport static com.calendarfx.view.CalendarView.Page.MONTH;\nimport static com.calendarfx.view.CalendarView.Page.WEEK;\nimport static com.calendarfx.view.CalendarView.Page.YEAR;\nimport static com.calendarfx.view.RequestEvent.REQUEST_DATE;\nimport static com.calendarfx.view.RequestEvent.REQUEST_DATE_TIME;\nimport static com.calendarfx.view.RequestEvent.REQUEST_ENTRY;\nimport static com.calendarfx.view.RequestEvent.REQUEST_WEEK;\nimport static com.calendarfx.view.RequestEvent.REQUEST_YEAR;\nimport static com.calendarfx.view.RequestEvent.REQUEST_YEAR_MONTH;\nimport static com.calendarfx.view.YearMonthView.ClickBehaviour.PERFORM_SELECTION;\nimport static javafx.geometry.Side.RIGHT;\nimport static javafx.scene.control.SelectionMode.SINGLE;\n\npublic class CalendarViewSkin extends SkinBase<CalendarView> {\n\n    private final MasterDetailPane leftMasterDetailPane;\n    private final ToggleButton trayButton;\n    private final Button addCalendarButton;\n    private final Button printButton;\n    private final SearchResultView searchResultView;\n    private final StackPane stackPane;\n\n    private final ToggleButton showYear;\n    private final ToggleButton showMonth;\n    private final ToggleButton showWeek;\n    private final ToggleButton showDay;\n    private final HBox leftToolBarBox;\n    private final SegmentedButton switcher;\n\n    private SourceView sourceView;\n\n    private final InvalidationListener entriesVisibilityListener = obs -> updateCalendarVisibility();\n    private final InvalidationListener weakEntriesVisibilityListener = new WeakInvalidationListener(entriesVisibilityListener);\n\n    private final InvalidationListener printEntriesVisibilityListener = obs -> updatePrintVisibility();\n    private final InvalidationListener weakPrintEntriesVisibilityListener = new WeakInvalidationListener(printEntriesVisibilityListener);\n\n    public CalendarViewSkin(CalendarView view) {\n        super(view);\n\n        if (Boolean.getBoolean(\"calendarfx.developer\")) {\n            view.addEventFilter(KeyEvent.KEY_PRESSED, evt -> {\n                if (evt.isMetaDown() && evt.getCode().equals(KeyCode.D)) {\n                    view.setShowDeveloperConsole(\n                            !view.isShowDeveloperConsole());\n                }\n            });\n        }\n\n        view.addEventHandler(REQUEST_DATE, evt -> maybeRunAndConsume(evt, e -> view.showDate(evt.getDate())));\n        view.addEventHandler(REQUEST_DATE_TIME, evt -> maybeRunAndConsume(evt, e -> view.showDateTime(evt.getDateTime())));\n        view.addEventHandler(REQUEST_WEEK, evt -> maybeRunAndConsume(evt, e -> view.showWeek(evt.getYear(), evt.getWeekOfYear())));\n        view.addEventHandler(REQUEST_YEAR_MONTH, evt -> maybeRunAndConsume(evt, e -> view.showYearMonth(evt.getYearMonth())));\n        view.addEventHandler(REQUEST_YEAR, evt -> maybeRunAndConsume(evt, e -> view.showYear(evt.getYear())));\n        view.addEventHandler(REQUEST_ENTRY, evt -> maybeRunAndConsume(evt, e -> view.getSelectedPageView().editEntry(evt.getEntry())));\n\n        view.getAvailablePages().addListener((Observable it) -> buildSwitcher());\n\n        TrayPane trayPane = new TrayPane();\n        this.trayButton = new ToggleButton(Messages.getString(\"CalendarViewSkin.TOGGLE_SOURCE_TRAY\"));\n        this.trayButton.setId(\"show-tray-button\");\n        this.trayButton.setMaxHeight(Double.MAX_VALUE);\n\n        FontIcon addIcon = new FontIcon(FontAwesome.PLUS);\n        addIcon.getStyleClass().addAll(\"button-icon\", \"add-calendar-button-icon\");\n\n        this.addCalendarButton = new Button();\n        this.addCalendarButton.setId(\"add-calendar-button\");\n        this.addCalendarButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n        this.addCalendarButton.setMaxHeight(Double.MAX_VALUE);\n        this.addCalendarButton.setGraphic(addIcon);\n        this.addCalendarButton.setOnAction(evt -> view.createCalendarSource());\n\n        FontIcon printIcon = new FontIcon(FontAwesome.PRINT);\n        printIcon.getStyleClass().addAll(\"button-icon\", \"print-button-icon\");\n\n        this.printButton = new Button();\n        this.printButton.setId(\"print-button\");\n        this.printButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n        this.printButton.setOnAction(evt -> print());\n        this.printButton.setMaxHeight(Double.MAX_VALUE);\n        this.printButton.setGraphic(printIcon);\n\n        this.leftMasterDetailPane = new MasterDetailPane(Side.LEFT);\n\n        if (view.isShowSourceTray()) {\n            openTray();\n        } else {\n            closeTray();\n        }\n\n        Bindings.bindBidirectional(trayButton.selectedProperty(), view.showSourceTrayProperty());\n\n        view.showSourceTrayProperty().addListener(it -> {\n            if (view.isShowSourceTray()) {\n                openTray();\n            } else {\n                closeTray();\n            }\n        });\n\n        Platform.runLater(() -> sourceView.getCalendarVisibilityMap().keySet().forEach(calendar -> sourceView.getCalendarVisibilityProperty(calendar).addListener(weakEntriesVisibilityListener)));\n\n        view.selectedPageProperty().addListener(weakEntriesVisibilityListener);\n\n        ColumnConstraints leftColumn = new ColumnConstraints();\n        ColumnConstraints centerColumn = new ColumnConstraints();\n        ColumnConstraints rightColumn = new ColumnConstraints();\n\n        leftColumn.setPercentWidth(35);\n        centerColumn.setPercentWidth(30);\n        rightColumn.setPercentWidth(35);\n\n        GridPane toolBarGridPane = new GridPane();\n        toolBarGridPane.setMinWidth(0);\n        toolBarGridPane.getColumnConstraints().addAll(leftColumn, centerColumn, rightColumn);\n        toolBarGridPane.setId(\"toolbar\");\n        toolBarGridPane.getStyleClass().add(\"tool-bar\");\n\n        /*\n         * Toolbar box on the left - hand side.Gets rebuild when some of the\n         * control 's properties change.\n         */\n        leftToolBarBox = new HBox();\n        leftToolBarBox.getStyleClass().add(\"left-toolbar-container\");\n        leftToolBarBox.setSpacing(5);\n\n        buildLeftToolBarBox();\n\n        InvalidationListener buildLeftToolBarBoxListener = it -> buildLeftToolBarBox();\n        view.showSourceTrayButtonProperty().addListener(buildLeftToolBarBoxListener);\n        view.showAddCalendarButtonProperty().addListener(buildLeftToolBarBoxListener);\n        view.showPrintButtonProperty().addListener(buildLeftToolBarBoxListener);\n        view.showPageToolBarControlsProperty().addListener(buildLeftToolBarBoxListener);\n        view.selectedPageProperty().addListener(buildLeftToolBarBoxListener);\n\n        toolBarGridPane.add(leftToolBarBox, 0, 0);\n\n        // toolbar center\n        showDay = new ToggleButton(Messages.getString(\"CalendarViewSkin.TOGGLE_SHOW_DAY\"));\n        showWeek = new ToggleButton(Messages.getString(\"CalendarViewSkin.TOGGLE_SHOW_WEEK\"));\n        showMonth = new ToggleButton(Messages.getString(\"CalendarViewSkin.TOGGLE_SHOW_MONTH\"));\n        showYear = new ToggleButton(Messages.getString(\"CalendarViewSkin.TOGGLE_SHOW_YEAR\"));\n\n        showDay.setOnAction(evt -> {\n            view.showDayPage();\n            updateToggleButtons();\n        });\n\n        showWeek.setOnAction(evt -> {\n            view.showWeekPage();\n            updateToggleButtons();\n        });\n\n        showMonth.setOnAction(evt -> {\n            view.showMonthPage();\n            updateToggleButtons();\n        });\n\n        showYear.setOnAction(evt -> {\n            view.showYearPage();\n            updateToggleButtons();\n        });\n\n        switcher = new SegmentedButton();\n        switcher.setId(\"switcher\");\n        switcher.visibleProperty().bind(view.showPageSwitcherProperty());\n        buildSwitcher();\n\n        GridPane centerToolBarBox = new GridPane();\n        GridPane.setHalignment(switcher, HPos.CENTER);\n        GridPane.setValignment(switcher, VPos.CENTER);\n        centerToolBarBox.add(switcher, 0, 0);\n        centerToolBarBox.setAlignment(Pos.CENTER);\n        toolBarGridPane.add(centerToolBarBox, 1, 0);\n\n        // tooltips\n        trayButton.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_SOURCE_TRAY\")));\n        addCalendarButton.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_ADD_CALENDAR\")));\n        printButton.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_PRINT\")));\n        showDay.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_SHOW_DAY\")));\n        showWeek.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_SHOW_WEEK\")));\n        showMonth.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_SHOW_MONTH\")));\n        showYear.setTooltip(new Tooltip(Messages.getString(\"CalendarViewSkin.TOOLTIP_SHOW_YEAR\")));\n\n        // toolbar right\n        FontIcon searchIcon = new FontIcon(FontAwesome.SEARCH);\n        searchIcon.setId(\"search-icon\");\n\n        SortedList<ZoneId> sortedZones = new SortedList<>(view.getAvailableZoneIds());\n        sortedZones.setComparator(new ZoneIdComparator());\n\n        ChoiceBox<ZoneId> zoneIdBox = new ChoiceBox<>();\n        zoneIdBox.setItems(sortedZones);\n        zoneIdBox.valueProperty().bindBidirectional(view.zoneIdProperty());\n        zoneIdBox.setConverter(new ZoneIdStringConverter());\n        zoneIdBox.visibleProperty().bind(view.enableTimeZoneSupportProperty());\n        zoneIdBox.managedProperty().bind(view.enableTimeZoneSupportProperty());\n\n        SearchTextField searchField = view.getSearchField();\n        searchField.setPrefColumnCount(20);\n        searchField.setLeft(searchIcon);\n        searchField.setId(\"search-field\");\n        searchField.setPromptText(Messages.getString(\"CalendarViewSkin.PROMPT_SEARCH_FIELD\"));\n\n        GridPane.setFillWidth(searchField, false);\n        GridPane.setHalignment(searchField, HPos.RIGHT);\n\n        HBox rightToolbarContainer = new HBox(zoneIdBox, searchField);\n        rightToolbarContainer.setAlignment(Pos.CENTER_RIGHT);\n        rightToolbarContainer.getStyleClass().add(\"right-toolbar-container\");\n        toolBarGridPane.add(rightToolbarContainer, 2, 0);\n\n        BorderPane borderPane1 = new BorderPane();\n        borderPane1.topProperty().bind(view.headerProperty());\n        borderPane1.setCenter(stackPane = new StackPane());\n        borderPane1.bottomProperty().bind(view.footerProperty());\n\n        view.selectedPageProperty().addListener(it -> changePage());\n\n        leftMasterDetailPane.setMasterNode(borderPane1);\n        leftMasterDetailPane.setDetailNode(trayPane);\n        leftMasterDetailPane.setId(\"tray-pane\");\n        leftMasterDetailPane.animatedProperty().bindBidirectional(view.traysAnimatedProperty());\n\n        MasterDetailPane rightMasterDetailPane = new MasterDetailPane(RIGHT);\n        searchResultView = view.getSearchResultView();\n\n        Bindings.bindContentBidirectional(searchResultView.getCalendarSources(), view.getCalendarSources());\n\n        searchResultView.zoneIdProperty().bind(view.zoneIdProperty());\n        searchResultView.searchTextProperty().bind(searchField.textProperty());\n        searchResultView.selectedEntryProperty().addListener(evt -> showSelectedSearchResult());\n\n        view.showSearchResultsTrayProperty().bind(Bindings.not(Bindings.isEmpty(searchResultView.getSearchResults())));\n\n        rightMasterDetailPane.setDetailNode(searchResultView);\n        rightMasterDetailPane.setMasterNode(leftMasterDetailPane);\n        rightMasterDetailPane.showDetailNodeProperty().bind(view.showSearchResultsTrayProperty());\n\n        BorderPane borderPane = new BorderPane();\n\n        if (view.isShowToolBar()) {\n            borderPane.setTop(toolBarGridPane);\n        }\n\n        view.showToolBarProperty().addListener(it -> {\n            if (view.isShowToolBar()) {\n                borderPane.setTop(toolBarGridPane);\n            } else {\n                borderPane.setTop(null);\n            }\n        });\n\n        borderPane.setCenter(rightMasterDetailPane);\n\n        if (Boolean.getBoolean(\"calendarfx.developer\")) {\n            DeveloperConsole developerConsole = view.getDeveloperConsole();\n            MasterDetailPane developerConsoleMasterDetailPane = new MasterDetailPane(Side.BOTTOM);\n            developerConsoleMasterDetailPane.setDividerPosition(.6);\n            developerConsoleMasterDetailPane.animatedProperty().bind(view.traysAnimatedProperty());\n            developerConsoleMasterDetailPane.getStyleClass().add(\"developer-master-detail-pane\");\n            developerConsoleMasterDetailPane.setDetailSide(Side.BOTTOM);\n            developerConsoleMasterDetailPane.setMasterNode(borderPane);\n            developerConsoleMasterDetailPane.setDetailNode(developerConsole);\n            developerConsoleMasterDetailPane.setShowDetailNode(true);\n            developerConsoleMasterDetailPane.showDetailNodeProperty().bind(view.showDeveloperConsoleProperty());\n            getChildren().add(developerConsoleMasterDetailPane);\n        } else {\n            getChildren().add(borderPane);\n        }\n\n        final PageBase selectedPage = view.getSelectedPageView();\n        selectedPage.toFront();\n\n        updateToggleButtons();\n    }\n\n    private void maybeRunAndConsume(RequestEvent evt, Consumer<RequestEvent> consumer) {\n        if (!evt.isConsumed()) {\n            consumer.accept(evt);\n            evt.consume();\n        }\n    }\n\n    private void openTray() {\n        leftMasterDetailPane.resetDividerPosition();\n        leftMasterDetailPane.setShowDetailNode(true);\n    }\n\n    private void closeTray() {\n        leftMasterDetailPane.setShowDetailNode(false);\n    }\n\n    /**\n     * Refresh entries from specific page <b>(Selected Page)</b>. It is called\n     * after change selected Page (ButtonSwitcher) or check/uncheck any\n     * CalendarSource.\n     */\n    private void updateCalendarVisibility() {\n        CalendarView view = getSkinnable();\n\n        if (view.getSelectedPage().equals(DAY)) {\n            view.getDayPage().refreshData();\n        } else if (view.getSelectedPage().equals(WEEK)) {\n            view.getWeekPage().refreshData();\n        }\n    }\n\n    /**\n     * Refresh entries in <b>PrintablePage</b>. It is called after change type\n     * of view or check/uncheck any CalendarSource in Print dialog.\n     */\n    private void updatePrintVisibility() {\n        PrintablePage printablePage = printView.getPreviewPane().getPrintablePage();\n\n        if (printablePage.getViewType() == ViewType.DAY_VIEW) {\n            printablePage.getDayView().refreshData();\n        } else if (printablePage.getViewType() == ViewType.WEEK_VIEW) {\n            printablePage.getWeekView().refreshData();\n        }\n    }\n\n    private void buildSwitcher() {\n        CalendarView view = getSkinnable();\n        switcher.getButtons().clear();\n        if (view.getAvailablePages().contains(DAY)) {\n            switcher.getButtons().add(showDay);\n        }\n        if (view.getAvailablePages().contains(WEEK)) {\n            switcher.getButtons().add(showWeek);\n        }\n        if (view.getAvailablePages().contains(MONTH)) {\n            switcher.getButtons().add(showMonth);\n        }\n        if (view.getAvailablePages().contains(YEAR)) {\n            switcher.getButtons().add(showYear);\n        }\n\n        // no need to show anything if there is only one page left\n        if (switcher.getButtons().size() == 1) {\n            switcher.getButtons().clear();\n        }\n    }\n\n    private void buildLeftToolBarBox() {\n        leftToolBarBox.getChildren().clear();\n\n        if (getSkinnable().isShowSourceTrayButton()) {\n            leftToolBarBox.getChildren().add(trayButton);\n        }\n\n        if (getSkinnable().isShowAddCalendarButton()) {\n            leftToolBarBox.getChildren().add(addCalendarButton);\n        }\n\n        if (!leftToolBarBox.getChildren().isEmpty() && getSkinnable().isShowPrintButton()) {\n            leftToolBarBox.getChildren().add(new Separator(Orientation.VERTICAL));\n        }\n\n        if (getSkinnable().isShowPrintButton()) {\n            leftToolBarBox.getChildren().add(printButton);\n        }\n\n        if (getSkinnable().isShowPageToolBarControls()) {\n            Page page = getSkinnable().getSelectedPage();\n            Node toolBarControls = getSkinnable().getPageView(page).getToolBarControls();\n\n            if (toolBarControls != null && !((toolBarControls instanceof Pane) && ((Pane) toolBarControls).getChildrenUnmodifiable().isEmpty())) {\n                if (!leftToolBarBox.getChildren().isEmpty()) {\n                    leftToolBarBox.getChildren().add(new Separator(Orientation.VERTICAL));\n                }\n                leftToolBarBox.getChildren().add(toolBarControls);\n            }\n        }\n    }\n\n    private void changePage() {\n        CalendarView view = getSkinnable();\n        updateToggleButtons();\n        stackPane.getChildren().setAll(view.getSelectedPageView());\n    }\n\n\n    private void updateToggleButtons() {\n        CalendarView view = getSkinnable();\n        Page page = view.getSelectedPage();\n        if (page.equals(DAY)) {\n            showDay.setSelected(true);\n        } else if (page.equals(WEEK)) {\n            showWeek.setSelected(true);\n        } else if (page.equals(MONTH)) {\n            showMonth.setSelected(true);\n        } else if (page.equals(YEAR)) {\n            showYear.setSelected(true);\n        }\n\n        stackPane.getChildren().setAll(view.getSelectedPageView());\n    }\n\n    private void showSelectedSearchResult() {\n        Entry<?> result = searchResultView.getSelectedEntry();\n        if (result != null) {\n            getSkinnable().showEntry(result);\n        }\n    }\n\n    class TrayPane extends BorderPane {\n\n        private final YearMonthView yearMonthView;\n\n        public TrayPane() {\n            // source view\n            sourceView = getSkinnable().getSourceView();\n            sourceView.bind(getSkinnable());\n\n            // year month view\n            yearMonthView = getSkinnable().getYearMonthView();\n            yearMonthView.setShowToday(true);\n            yearMonthView.setShowTodayButton(false);\n            yearMonthView.setId(\"date-picker\");\n            yearMonthView.setSelectionMode(SINGLE);\n            yearMonthView.setClickBehaviour(PERFORM_SELECTION);\n            yearMonthView.getSelectedDates().add(getSkinnable().getDate());\n            yearMonthView.getSelectedDates().addListener((Observable evt) -> {\n                if (!yearMonthView.getSelectedDates().isEmpty()) {\n                    yearMonthView.setDate(yearMonthView.getSelectedDates().iterator().next());\n                }\n            });\n\n            getSkinnable().dateProperty().addListener(it -> {\n                yearMonthView.getSelectedDates().clear();\n                yearMonthView.getSelectedDates().add(getSkinnable().getDate());\n            });\n\n            Bindings.bindBidirectional(yearMonthView.todayProperty(), getSkinnable().todayProperty());\n            Bindings.bindBidirectional(yearMonthView.dateProperty(), getSkinnable().dateProperty());\n            yearMonthView.weekFieldsProperty().bind(getSkinnable().weekFieldsProperty());\n\n            ScrollPane scrollPane = new ScrollPane(sourceView);\n\n            scrollPane.getStyleClass().add(\"source-view-scroll-pane\");\n            setCenter(scrollPane);\n            setBottom(yearMonthView);\n        }\n    }\n\n    private PrintView printView;\n\n    private void print() {\n        if (printView == null) {\n            printView = getSkinnable().getPrintView();\n            printView.dateProperty().bind(getSkinnable().dateProperty());\n            printView.zoneIdProperty().bind(getSkinnable().zoneIdProperty());\n        }\n\n        printView.setToday(getSkinnable().getToday());\n        printView.getPreviewPane().getPrintablePage().setPageStartDate(getSkinnable().getDate());\n\n        printView.setWeekFields(getSkinnable().getWeekFields());\n        printView.getCalendarSources().setAll(getSkinnable().getCalendarSources());\n        printView.setLayout(getSkinnable().getSelectedPageView().getLayout());\n        printView.setViewType(getSkinnable().getSelectedPageView().getPrintViewType());\n        printView.loadDropDownValues(getSkinnable().getDate());\n\n        printView.show(getSkinnable().getScene().getWindow());\n\n        Platform.runLater(() -> {\n\n            SourceView printSource = printView.getSettingsView().getSourceView();\n\n            for (Calendar calendar : printSource.getCalendarVisibilityMap().keySet()) {\n                printSource.getCalendarVisibilityProperty(calendar).removeListener(weakPrintEntriesVisibilityListener);\n                printSource.getCalendarVisibilityProperty(calendar).addListener(weakPrintEntriesVisibilityListener);\n            }\n\n        });\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DataLoader.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.LoadEvent;\nimport com.calendarfx.util.LoggingDomain;\n\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.stream.Collectors;\n\nimport static java.util.Objects.requireNonNull;\n\npublic final class DataLoader {\n\n    private final LoadDataSettingsProvider provider;\n\n    public DataLoader(LoadDataSettingsProvider provider) {\n        this.provider = requireNonNull(provider);\n    }\n\n    public void loadEntries(Map<LocalDate, List<Entry<?>>> result) {\n\n        long time = System.currentTimeMillis();\n\n        LocalDate startDate = provider.getLoadStartDate();\n        LocalDate endDate = provider.getLoadEndDate();\n\n        ZoneId zoneId = provider.getZoneId();\n\n        for (CalendarSource source : provider.getCalendarSources()) {\n\n            for (Calendar calendar : source.getCalendars()\n                    .stream()\n                    .filter(c -> provider.isCalendarVisible(c))\n                    .collect(Collectors.toList())) {\n\n                try {\n                    Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(startDate, endDate, zoneId);\n\n                    for (LocalDate date : entries.keySet()) {\n\n                        List<Entry<?>> list = result.computeIfAbsent(date, it -> new ArrayList<>());\n                        List<Entry<?>> dateEntries = entries.get(date);\n                        if (dateEntries != null) {\n                            for (Entry<?> entry : dateEntries) {\n                                if (entry == null) {\n                                    if (LoggingDomain.MODEL.isLoggable(Level.SEVERE)) {\n                                        LoggingDomain.MODEL.severe(\"the calendar \" + calendar.getName() + \" (source \" + source.getName() + \") returned a NULL entry\");\n                                    }\n                                } else {\n                                    list.add(entry);\n                                }\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        for (List<Entry<?>> entries : result.values()) {\n            Collections.sort(entries);\n        }\n\n        LoggingDomain.PERFORMANCE.fine(\"data load time: \" + (System.currentTimeMillis() - time) + \", view = \" + provider.getClass().getSimpleName());\n\n        provider.getControl().fireEvent(new LoadEvent(LoadEvent.LOAD, provider.getLoaderName(), provider.getCalendarSources(), startDate, endDate, zoneId));\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DateControlSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DraggedEntry;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.ListChangeListener.Change;\nimport javafx.collections.MapChangeListener;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\nimport javafx.event.WeakEventHandler;\nimport javafx.scene.control.SkinBase;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.logging.Level;\n\nimport static com.calendarfx.model.CalendarEvent.CALENDAR_CHANGED;\nimport static javafx.scene.input.MouseEvent.MOUSE_PRESSED;\n\npublic abstract class DateControlSkin<C extends DateControl> extends SkinBase<C> {\n\n   private final InvalidationListener calendarVisibilityListener = it -> calendarVisibilityChanged();\n\n    private final WeakInvalidationListener weakCalendarVisibilityListener = new WeakInvalidationListener(calendarVisibilityListener);\n\n    public DateControlSkin(C control) {\n        super(control);\n\n        control.zoneIdProperty().addListener(it -> zoneIdChanged());\n\n        control.addEventHandler(MOUSE_PRESSED, evt -> control.clearSelection());\n\n        control.getCalendars().addListener(this::calendarListChanged);\n\n        for (Calendar calendar : control.getCalendars()) {\n            calendar.addEventHandler(weakCalendarListener);\n        }\n\n        MapChangeListener<? super Object, ? super Object> propertiesListener = change -> {\n            if (change.wasAdded()) {\n                if (change.getKey().equals(\"refresh.data\")) {\n                    LoggingDomain.VIEW.fine(\"data refresh was requested by the application\");\n                    control.getProperties().remove(\"refresh.data\");\n                    refreshData();\n                }\n            }\n        };\n\n        control.getProperties().addListener(propertiesListener);\n\n        for (Calendar calendar : control.getCalendars()) {\n            control.getCalendarVisibilityProperty(calendar).addListener(weakCalendarVisibilityListener);\n        }\n\n        control.getCalendars().addListener((Change<? extends Calendar> change) -> {\n            while (change.next()) {\n                if (change.wasAdded()) {\n                    for (Calendar calendar : change.getAddedSubList()) {\n                        control.getCalendarVisibilityProperty(calendar).addListener(weakCalendarVisibilityListener);\n                    }\n                } else if (change.wasRemoved()) {\n                    for (Calendar calendar : change.getRemoved()) {\n                        control.getCalendarVisibilityProperty(calendar).removeListener(weakCalendarVisibilityListener);\n                    }\n                }\n            }\n        });\n    }\n\n    protected void refreshData() {\n    }\n\n    protected void calendarVisibilityChanged() {\n    }\n\n    private final EventHandler<CalendarEvent> calendarListener = this::calendarChanged;\n\n    private final WeakEventHandler<CalendarEvent> weakCalendarListener = new WeakEventHandler<>(calendarListener);\n\n    private void calendarListChanged(Change<? extends Calendar> change) {\n        C dateControl = getSkinnable();\n        while (change.next()) {\n            if (change.wasAdded()) {\n                for (Calendar calendar : change.getAddedSubList()) {\n                    calendar.addEventHandler(weakCalendarListener);\n                    dateControl.getCalendarVisibilityProperty(calendar).addListener(weakCalendarVisibilityListener);\n                }\n            } else if (change.wasRemoved()) {\n                for (Calendar calendar : change.getRemoved()) {\n                    calendar.removeEventHandler(weakCalendarListener);\n                    dateControl.getCalendarVisibilityProperty(calendar).removeListener(weakCalendarVisibilityListener);\n                }\n            }\n        }\n    }\n\n    protected void zoneIdChanged() {\n    }\n\n    private void calendarChanged(CalendarEvent evt) {\n\n        if (LoggingDomain.EVENTS.isLoggable(Level.FINE) && !(evt.getEntry() instanceof DraggedEntry)) {\n            LoggingDomain.EVENTS.fine(\"calendar event in \" + getSkinnable().getClass().getSimpleName() + \": \" + evt.getEventType() + \", details: \" + evt);\n        }\n\n        if (getSkinnable().isSuspendUpdates()) {\n            return;\n        }\n\n        if (evt.getEventType().getSuperType().equals(CalendarEvent.ENTRY_CHANGED) && evt.getEntry().isRecurrence()) {\n            return;\n        }\n\n        Util.runInFXThread(() -> {\n            EventType<? extends Event> eventType = evt.getEventType();\n            if (eventType.equals(CalendarEvent.ENTRY_INTERVAL_CHANGED)) {\n                entryIntervalChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_FULL_DAY_CHANGED)) {\n                entryFullDayChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_RECURRENCE_RULE_CHANGED)) {\n                entryRecurrenceRuleChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_CALENDAR_CHANGED)) {\n                entryCalendarChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_TITLE_CHANGED)) {\n                entryTitleChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_LOCATION_CHANGED)) {\n                entryLocationChanged(evt);\n            } else if (eventType.equals(CalendarEvent.ENTRY_USER_OBJECT_CHANGED)) {\n                entryUserObjectChanged(evt);\n            } else if (eventType.equals(CALENDAR_CHANGED)) {\n                calendarChanged(evt.getCalendar());\n            }\n        });\n    }\n\n    protected void entryIntervalChanged(CalendarEvent evt) {\n    }\n\n    protected void entryFullDayChanged(CalendarEvent evt) {\n    }\n\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n    }\n\n    protected void entryCalendarChanged(CalendarEvent evt) {\n    }\n\n    protected void entryTitleChanged(CalendarEvent evt) {\n    }\n\n    protected void entryLocationChanged(CalendarEvent evt) {\n    }\n\n    protected void entryUserObjectChanged(CalendarEvent evt) {\n    }\n\n    protected void calendarChanged(Calendar calendar) {\n    }\n\n    protected boolean isRelevant(Entry<?> entry) {\n\n        if (this instanceof LoadDataSettingsProvider) {\n            C dateControl = getSkinnable();\n\n            if (!(entry instanceof DraggedEntry) && !dateControl.isCalendarVisible(entry.getCalendar())) {\n                return false;\n            }\n\n            LoadDataSettingsProvider provider = (LoadDataSettingsProvider) this;\n\n            ZoneId zoneId = getSkinnable().getZoneId();\n            LocalDate loadStartDate = provider.getLoadStartDate();\n            LocalDate loadEndDate = provider.getLoadEndDate();\n\n            return entry.isShowing(loadStartDate, loadEndDate, zoneId);\n        }\n\n        return false;\n    }\n\n    protected boolean isRelevant(Interval interval) {\n        LoadDataSettingsProvider provider = (LoadDataSettingsProvider) this;\n        ZoneId zoneId = getSkinnable().getZoneId();\n        LocalDate loadStartDate = provider.getLoadStartDate();\n        LocalDate loadEndDate = provider.getLoadEndDate();\n        ZonedDateTime st = ZonedDateTime.of(loadStartDate, LocalTime.MIN, zoneId);\n        ZonedDateTime et = ZonedDateTime.of(loadEndDate, LocalTime.MAX, zoneId);\n        return Util.intersect(interval.getStartZonedDateTime(), interval.getEndZonedDateTime(), st, et);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DayEntryViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DraggedEntry;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.ObservableMap;\nimport javafx.geometry.Orientation;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.FlowPane;\nimport javafx.scene.layout.Region;\nimport javafx.scene.shape.Rectangle;\n\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.time.format.TextStyle;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * The default day entry view.\n * It displays a title and the start time of the entry.\n */\npublic class DayEntryViewSkin extends SkinBase<DayEntryView> {\n\n    private final DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n\n    protected Label startTimeLabel;\n    protected Label titleLabel;\n\n    private final InvalidationListener updateStylesListener = it -> updateStyles();\n    private final WeakInvalidationListener weakUpdateStylesListener = new WeakInvalidationListener(updateStylesListener);\n\n    private final InvalidationListener updateLabelsListener = it -> updateLabels();\n    private final WeakInvalidationListener weakUpdateLabelsListener = new WeakInvalidationListener(updateLabelsListener);\n\n    public DayEntryViewSkin(DayEntryView view) {\n        super(view);\n\n        startTimeLabel = createStartTimeLabel();\n        startTimeLabel.setManaged(false);\n        startTimeLabel.setMouseTransparent(true);\n\n        titleLabel = createTitleLabel();\n        titleLabel.setManaged(false);\n        titleLabel.setMouseTransparent(true);\n\n        getChildren().addAll(startTimeLabel, titleLabel);\n\n        Entry<?> entry = getEntry();\n\n        entry.intervalProperty().addListener(weakUpdateLabelsListener);\n        entry.calendarProperty().addListener(weakUpdateStylesListener);\n        entry.titleProperty().addListener(weakUpdateLabelsListener);\n\n        getSkinnable().positionProperty().addListener(weakUpdateLabelsListener);\n        updateLabels();\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(view.widthProperty());\n        clip.heightProperty().bind(view.heightProperty());\n        view.setClip(clip);\n\n        view.nodesProperty().addListener((Observable it) -> updateNodes());\n        updateStyles();\n        updateNodes();\n    }\n\n    private void updateNodes() {\n        final DayEntryView view = getSkinnable();\n        final ObservableMap<Pos, List<Node>> nodes = view.getNodes();\n\n        if (nodes != null) {\n            Set<Pos> previouslyUsedPositions = null;\n            if (nodePanes != null) {\n                previouslyUsedPositions = nodePanes.keySet();\n            }\n\n            final Set<Pos> currentlyUsedPositions = nodes.keySet();\n\n            // we have to remove some previously used flow panes from the entry view\n            if (previouslyUsedPositions != null) {\n                previouslyUsedPositions.removeAll(currentlyUsedPositions);\n                for (Pos next : previouslyUsedPositions) {\n                    final FlowPane removedPane = nodePanes.remove(next);\n                    getChildren().remove(removedPane);\n                }\n            }\n\n            currentlyUsedPositions.forEach(pos -> createPosition(pos, nodes.get(pos)));\n        }\n\n        if (nodePanes != null && nodePanes.isEmpty()) {\n            nodePanes = null;\n        }\n    }\n\n    private Map<Pos, FlowPane> nodePanes;\n\n    private void createPosition(Pos pos, List<Node> nodes) {\n        if (nodePanes == null) {\n            nodePanes = new HashMap<>();\n        }\n\n        FlowPane flowPane = nodePanes.computeIfAbsent(pos, p -> {\n            FlowPane pane = new FlowPane();\n            pane.getStyleClass().add(\"icon-flow-pane\");\n            pane.setPrefWidth(Region.USE_COMPUTED_SIZE);\n            pane.setPrefHeight(Region.USE_COMPUTED_SIZE);\n            pane.setManaged(false);\n            pane.setMouseTransparent(true);\n\n            switch (pos) {\n                case TOP_CENTER:\n                case CENTER:\n                case BOTTOM_RIGHT:\n                case BASELINE_LEFT:\n                case BASELINE_CENTER:\n                case BOTTOM_LEFT:\n                case BOTTOM_CENTER:\n                case BASELINE_RIGHT:\n                    pane.setOrientation(Orientation.HORIZONTAL);\n                    pane.prefWrapLengthProperty().bind(getSkinnable().widthProperty());\n                    break;\n                case TOP_LEFT:\n                case TOP_RIGHT:\n                case CENTER_LEFT:\n                case CENTER_RIGHT:\n                    pane.setOrientation(Orientation.VERTICAL);\n                    pane.prefWrapLengthProperty().bind(getSkinnable().heightProperty());\n                    break;\n            }\n\n            pane.setAlignment(pos);\n\n            getChildren().add(pane);\n            return pane;\n        });\n\n        flowPane.getChildren().setAll(nodes);\n    }\n\n    /**\n     * @return The entry.\n     */\n    protected Entry<?> getEntry() {\n        Entry<?> entry = getSkinnable().getEntry();\n        if (entry.isRecurrence()) {\n            entry = entry.getRecurrenceSourceEntry();\n        }\n        return entry;\n    }\n\n    /**\n     * This method updates the styles of the node according to the entry\n     * settings.\n     */\n    protected void updateStyles() {\n        DayEntryView view = getSkinnable();\n        Entry<?> entry = getEntry();\n\n        Calendar calendar = entry.getCalendar();\n        if (entry instanceof DraggedEntry) {\n            calendar = ((DraggedEntry) entry).getOriginalCalendar();\n        }\n\n        // when the entry gets removed from its calendar then the calendar can\n        // be null\n        if (calendar == null) {\n            return;\n        }\n\n        view.getStyleClass().setAll(\"default-style-entry\", calendar.getStyle() + \"-entry\");\n\n        if (entry.isRecurrence()) {\n            view.getStyleClass().add(\"recurrence\");\n        }\n\n        view.getStyleClass().addAll(entry.getStyleClass());\n\n        startTimeLabel.getStyleClass().setAll(\"start-time-label\", \"default-style-entry-time-label\", calendar.getStyle() + \"-entry-time-label\");\n        titleLabel.getStyleClass().setAll(\"title-label\", \"default-style-entry-title-label\", calendar.getStyle() + \"-entry-title-label\");\n    }\n\n    /**\n     * The label used to show the start time.\n     *\n     * @return The label component.\n     */\n    protected Label createStartTimeLabel() {\n        Label label = new Label();\n        label.setMinSize(0, 0);\n        return label;\n    }\n\n    /**\n     * Convert the given time to a string.\n     *\n     * @param time the time\n     * @return The formatted time.\n     */\n    protected String formatTime(ZonedDateTime time, ZoneId zoneId) {\n        return formatter.format(time.withZoneSameInstant(zoneId));\n    }\n\n    /**\n     * Convert the given title. This method can be overridden for e.g.\n     * translating the title.\n     *\n     * @param title the title\n     * @return The formatted title.\n     */\n    protected String formatTitle(String title) {\n        return title;\n    }\n\n    /**\n     * The label used to show the title.\n     *\n     * @return The title component.\n     */\n    protected Label createTitleLabel() {\n        Label label = new Label();\n        label.setWrapText(true);\n        label.setMinSize(0, 0);\n\n        return label;\n    }\n\n    /**\n     * This method will be called if the labels need to be updated.\n     */\n    protected void updateLabels() {\n        Entry<?> entry = getEntry();\n\n        DayView dateControl = getSkinnable().getDateControl();\n        ZonedDateTime startTime = entry.getStartAsZonedDateTime();\n\n        if (!Objects.equals(dateControl.getZoneId(), entry.getZoneId())) {\n            startTimeLabel.setText(formatTime(startTime, dateControl.getZoneId()) + \" (\" + formatTime(startTime, entry.getZoneId()) + \" \" + entry.getZoneId().getDisplayName(TextStyle.SHORT, Locale.getDefault()) + \")\");\n        } else {\n            startTimeLabel.setText(formatTime(startTime, dateControl.getZoneId()));\n        }\n        titleLabel.setText(formatTitle(entry.getTitle()));\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        // title label\n        double titleHeight = titleLabel.prefHeight(contentWidth);\n\n        // it is guaranteed that we have enough height to display the title (see\n        // \"computeMinHeight\")\n        titleLabel.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY), snapSizeX(contentWidth), snapSizeY(titleHeight));\n\n        // start time label\n        double timeLabelHeight = startTimeLabel.prefHeight(contentWidth);\n        if (contentHeight - titleHeight > timeLabelHeight) {\n            startTimeLabel.setVisible(true);\n            startTimeLabel.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY + titleHeight), snapSizeX(contentWidth), snapSizeY(timeLabelHeight));\n        } else {\n            startTimeLabel.setVisible(false);\n        }\n\n        if (nodePanes != null) {\n            nodePanes.keySet().forEach(pos -> layoutNodesPane(pos, nodePanes.get(pos), contentX, contentY, contentWidth, contentHeight));\n        }\n    }\n\n    private void layoutNodesPane(Pos pos, FlowPane flowPane, double x, double y, double w, double h) {\n        double pw = flowPane.prefWidth(-1);\n        double ph = flowPane.prefHeight(-1);\n\n        switch (pos) {\n            case TOP_LEFT:\n                flowPane.resizeRelocate(x, y, pw, ph);\n                break;\n            case TOP_CENTER:\n                flowPane.resizeRelocate(x + w / 2 - pw / 2, y, pw, ph);\n                break;\n            case TOP_RIGHT:\n                flowPane.resizeRelocate(x + w - pw, y, pw, ph);\n                break;\n            case BOTTOM_LEFT:\n            case BASELINE_LEFT:\n                flowPane.resizeRelocate(x, h - ph, pw, ph);\n                break;\n            case BOTTOM_CENTER:\n            case BASELINE_CENTER:\n                flowPane.resizeRelocate(x + w / 2 - pw / 2, h - ph, pw, ph);\n                break;\n            case BOTTOM_RIGHT:\n            case BASELINE_RIGHT:\n                flowPane.resizeRelocate(x + w - pw, h - ph, pw, ph);\n                break;\n            case CENTER_LEFT:\n                flowPane.resizeRelocate(x, y + h / 2 - ph / 2, pw, ph);\n                break;\n            case CENTER:\n                flowPane.resizeRelocate(x + w / 2 - pw / 2, y + h / 2 - ph / 2, pw, ph);\n                break;\n            case CENTER_RIGHT:\n                flowPane.resizeRelocate(x + w - pw, y + h / 2 - ph / 2, pw, ph);\n                break;\n        }\n    }\n\n    @Override\n    protected double computeMinHeight(double width, double topInset,\n                                      double rightInset, double bottomInset, double leftInset) {\n        if (titleLabel != null && getSkinnable().isMinHeightEqualToTitleHeight()) {\n            // For this pref height calculation we do not consider the available\n            // width because we only want to show a single line of text.\n            return titleLabel.prefHeight(-1) + topInset + bottomInset;\n        }\n\n        return super.computeMinHeight(width, topInset, rightInset, bottomInset, leftInset);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewBaseSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\n\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\n\n@SuppressWarnings(\"javadoc\")\npublic class DayViewBaseSkin<T extends DayViewBase> extends DateControlSkin<T> {\n\n    private final InvalidationListener layoutListener = it -> getSkinnable().requestLayout();\n\n    public DayViewBaseSkin(T view) {\n        super(view);\n\n        registerLayoutListener(view.timeProperty());\n        registerLayoutListener(view.hourHeightProperty());\n        registerLayoutListener(view.hourHeightCompressedProperty());\n        registerLayoutListener(view.visibleHoursProperty());\n        registerLayoutListener(view.earlyLateHoursStrategyProperty());\n        registerLayoutListener(view.hoursLayoutStrategyProperty());\n        registerLayoutListener(view.startTimeProperty());\n        registerLayoutListener(view.endTimeProperty());\n        registerLayoutListener(view.getCalendars());\n        registerLayoutListener(view.enableCurrentTimeMarkerProperty());\n        registerLayoutListener(view.entryWidthPercentageProperty());\n        registerLayoutListener(view.showTodayProperty());\n        registerLayoutListener(view.zoneIdProperty());\n        registerLayoutListener(view.editAvailabilityProperty());\n        registerLayoutListener(view.scrollingEnabledProperty());\n        registerLayoutListener(view.gridTypeProperty());\n\n        view.setOnScroll(evt -> {\n            final double oldLocation = evt.getY();\n            final ZonedDateTime time = view.getZonedDateTimeAt(0, oldLocation, view.getZoneId());\n\n            if (view.isScrollingEnabled()) {\n                if (evt.isShortcutDown()) {\n                    // first compute new hour height\n                    final double delta = Math.max(-5, Math.min(5, evt.getDeltaY()));\n                    view.setHourHeight(Math.min(getSkinnable().getMaxHourHeight(), Math.max(getSkinnable().getMinHourHeight(), view.getHourHeight() + delta)));\n\n                    // then adjust scroll time to make sure the time found at mouse location stays where it is\n                    final double newLocation = view.getLocation(time);\n                    final double locationDelta = newLocation - oldLocation;\n                    final ZonedDateTime newScrollTime = view.getZonedDateTimeAt(0, locationDelta, view.getZoneId());\n                    view.setScrollTime(newScrollTime);\n\n                } else {\n                    view.setScrollTime(getSkinnable().getZonedDateTimeAt(0, -evt.getDeltaY(), view.getZoneId()));\n                }\n            }\n        });\n    }\n\n    private void registerLayoutListener(Observable obs) {\n        obs.addListener(layoutListener);\n    }\n\n    @Override\n    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n\n        if (getSkinnable().isScrollingEnabled()) {\n            return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);\n        }\n\n        T dayView = getSkinnable();\n        double hourHeight = dayView.getHourHeight();\n\n        EarlyLateHoursStrategy strategy = dayView.getEarlyLateHoursStrategy();\n\n        switch (strategy) {\n            case HIDE:\n                long earlyHours = ChronoUnit.HOURS.between(LocalTime.MIN, dayView.getStartTime());\n                long lateHours = ChronoUnit.HOURS.between(dayView.getEndTime(), LocalTime.MAX) + 1;\n                long hours = 24 - earlyHours - lateHours;\n                return hours * hourHeight;\n\n            case SHOW:\n                return 24 * hourHeight;\n\n            case SHOW_COMPRESSED:\n                earlyHours = ChronoUnit.HOURS.between(LocalTime.MIN, dayView.getStartTime());\n                lateHours = ChronoUnit.HOURS.between(dayView.getEndTime(), LocalTime.MAX) + 1;\n                hours = 24 - earlyHours - lateHours;\n                double hourHeightCompressed = dayView.getHourHeightCompressed();\n                return hours * hourHeight + (earlyHours + lateHours) * hourHeightCompressed;\n\n            default:\n                throw new IllegalArgumentException(\"unsupported early / late hours strategy: \" + strategy);\n        }\n    }\n\n    @Override\n    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        if (getSkinnable().isScrollingEnabled()) {\n            return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);\n        }\n\n        return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);\n    }\n\n    @Override\n    protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        if (getSkinnable().isScrollingEnabled()) {\n            return super.computeMinHeight(width, topInset, rightInset, bottomInset, leftInset);\n        }\n\n        return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewEditController.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.DateControl.EditOperation;\nimport com.calendarfx.view.DateControl.EntryEditParameter;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.view.DraggedEntry;\nimport com.calendarfx.view.DraggedEntry.DragMode;\nimport com.calendarfx.view.EntryViewBase;\nimport com.calendarfx.view.EntryViewBase.HeightLayoutStrategy;\nimport com.calendarfx.view.RequestEvent;\nimport com.calendarfx.view.VirtualGrid;\nimport com.calendarfx.view.WeekView;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.event.EventHandler;\nimport javafx.scene.Cursor;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\nimport javafx.util.Callback;\n\nimport java.time.DayOfWeek;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\nimport java.util.logging.Logger;\n\npublic class DayViewEditController {\n\n    private static final Logger LOGGER = LoggingDomain.EDITING;\n\n    private final DayViewBase view;\n    private DayEntryView dayEntryView;\n    private Entry<?> entry;\n    private DragMode dragMode;\n    private Handle handle;\n    private Duration offsetDuration;\n    private Duration entryDuration;\n\n    public DayViewEditController(DayViewBase dayView) {\n        this.view = Objects.requireNonNull(dayView);\n\n        dayView.addEventFilter(MouseEvent.MOUSE_CLICKED, this::mouseClicked);\n        dayView.addEventFilter(MouseEvent.MOUSE_PRESSED, this::mousePressed);\n        dayView.addEventFilter(MouseEvent.MOUSE_DRAGGED, this::mouseDragged);\n\n        final EventHandler<MouseEvent> mouseReleasedHandler = this::mouseReleased;\n\n        // mouse released is very important for us. register with the scene, so we get that in any case.\n        if (dayView.getScene() != null) {\n            dayView.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseReleasedHandler);\n        }\n\n        // also register with the scene property. Mostly to remove our event filter if the component gets destroyed.\n        dayView.sceneProperty().addListener(((observable, oldScene, newScene) -> {\n            if (oldScene != null) {\n                oldScene.removeEventFilter(MouseEvent.MOUSE_RELEASED, mouseReleasedHandler);\n            }\n            if (newScene != null) {\n                newScene.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseReleasedHandler);\n            }\n        }));\n        dayView.addEventFilter(MouseEvent.MOUSE_MOVED, this::mouseMoved);\n\n        lassoStartProperty().addListener(it -> dayView.setLassoStart(getLassoStart()));\n        lassoEndProperty().addListener(it -> dayView.setLassoEnd(getLassoEnd()));\n\n        setOnLassoFinished(dayView.getOnLassoFinished());\n        dayView.onLassoFinishedProperty().addListener(it -> setOnLassoFinished(dayView.getOnLassoFinished()));\n    }\n\n    private enum Handle {\n        TOP,\n        CENTER,\n        BOTTOM\n    }\n\n    private boolean initDragModeAndHandle(MouseEvent evt) {\n        dragMode = null;\n        handle = null;\n\n        /*\n         * On touch screen devices we can not edit entries.\n         */\n        if (evt.isSynthesized()) {\n            return false;\n        }\n\n        if (!(evt.getTarget() instanceof EntryViewBase)) {\n            return false;\n        }\n\n        dayEntryView = (DayEntryView) evt.getTarget();\n        entry = dayEntryView.getEntry();\n\n        Calendar calendar = entry.getCalendar();\n        if (calendar != null && calendar.isReadOnly()) {\n            return false;\n        }\n\n        double y = evt.getY() - dayEntryView.getBoundsInParent().getMinY();\n\n        LOGGER.finer(\"y-coordinate inside entry view: \" + y);\n\n        if (y > dayEntryView.getHeight() - 5) {\n            if (dayEntryView.getHeightLayoutStrategy().equals(HeightLayoutStrategy.USE_START_AND_END_TIME) && view.getEntryEditPolicy().call(new EntryEditParameter(view, entry, EditOperation.CHANGE_END))) {\n                dragMode = DragMode.END_TIME;\n                handle = Handle.BOTTOM;\n            }\n        } else if (y < 5) {\n            if (dayEntryView.getHeightLayoutStrategy().equals(HeightLayoutStrategy.USE_START_AND_END_TIME) && view.getEntryEditPolicy().call(new EntryEditParameter(view, entry, EditOperation.CHANGE_START))) {\n                dragMode = DragMode.START_TIME;\n                handle = Handle.TOP;\n            }\n        } else {\n            if (view.getEntryEditPolicy().call(new EntryEditParameter(view, entry, EditOperation.MOVE))) {\n                dragMode = DragMode.START_AND_END_TIME;\n                handle = Handle.CENTER;\n            }\n        }\n\n        return entry != null && dragMode != null && handle != null;\n    }\n\n    private void mouseMoved(MouseEvent evt) {\n        initDragModeAndHandle(evt);\n\n        if (dayEntryView != null) {\n            if (handle == null) {\n                dayEntryView.setCursor(Cursor.DEFAULT);\n                return;\n            }\n\n            switch (handle) {\n                case TOP:\n                    dayEntryView.setCursor(Cursor.N_RESIZE);\n                    break;\n                case BOTTOM:\n                    dayEntryView.setCursor(Cursor.S_RESIZE);\n                    break;\n                case CENTER:\n                    dayEntryView.setCursor(Cursor.MOVE);\n                    break;\n                default:\n                    dayEntryView.setCursor(Cursor.DEFAULT);\n                    break;\n            }\n        }\n    }\n\n    private enum Operation {\n        NONE,\n        EDIT_ENTRY,\n        EDIT_AVAILABILITY,\n        CREATE_ENTRY\n    }\n\n    private void mouseClicked(MouseEvent evt) {\n        // standard checks\n        if (evt.isConsumed() || !evt.isStillSincePress() || !evt.getButton().equals(MouseButton.PRIMARY)) {\n            return;\n        }\n\n        // do not create new entries while the user is editing the availability calendar\n        if (view.isEditAvailability()) {\n            return;\n        }\n\n        // mouse clicks only work on day views, not on entry views\n        if (!(evt.getTarget() instanceof DayViewBase)) {\n            return;\n        }\n\n        if (evt.getClickCount() == view.getCreateEntryClickCount()) {\n            Entry<?> entry = createEntryAt(evt);\n            evt.consume();\n\n            if (entry != null && view.isShowDetailsUponEntryCreation()) {\n                view.fireEvent(new RequestEvent(view, view, entry));\n            }\n        }\n    }\n\n    private Operation operation = Operation.NONE;\n\n    private MouseEvent mousePressedEvent;\n\n    private void mousePressed(MouseEvent evt) {\n        if (evt.isConsumed() || !evt.getButton().equals(MouseButton.PRIMARY)) {\n            return;\n        }\n\n        mousePressedEvent = evt;\n\n        entry = null;\n\n        LOGGER.finer(\"mouse event source: \" + evt.getSource());\n        LOGGER.finer(\"mouse event target: \" + evt.getTarget());\n        LOGGER.finer(\"mouse event y-coordinate:\" + evt.getY());\n        LOGGER.finer(\"time: \" + view.getZonedDateTimeAt(evt.getX(), evt.getY(), view.getZoneId()));\n\n        if (evt.getTarget() instanceof DayViewBase) {\n            mousePressedOnDayView(evt);\n        } else if (evt.getTarget() instanceof EntryViewBase) {\n            if (view.isEditAvailability()) {\n                if (!view.isScrollingEnabled()) {\n                    mousePressedEditAvailability(evt);\n                }\n            } else {\n                mousePressedEditEntry(evt);\n            }\n        }\n    }\n\n    private boolean entryEditingAllowed;\n\n    private void mousePressedEditEntry(MouseEvent evt) {\n        operation = Operation.EDIT_ENTRY;\n\n        entryEditingAllowed = false;\n\n        dragMode = null;\n        handle = null;\n\n        LOGGER.finer(\"mouse event source: \" + evt.getSource());\n        LOGGER.finer(\"mouse event target: \" + evt.getTarget());\n        LOGGER.finer(\"mouse event y-coordinate:\" + evt.getY());\n        LOGGER.finer(\"time: \" + view.getZonedDateTimeAt(evt.getX(), evt.getY(), view.getZoneId()));\n\n        boolean successfulInitialization = initDragModeAndHandle(evt);\n\n        if (successfulInitialization) {\n\n            LOGGER.finer(\"drag mode: \" + dragMode);\n            LOGGER.finer(\"handle: \" + handle);\n\n            Callback<EntryEditParameter, Boolean> entryEditPolicy = view.getEntryEditPolicy();\n\n            boolean operationAllowed = false;\n\n            switch (dragMode) {\n                case START_AND_END_TIME:\n                    if (entryEditPolicy.call(new EntryEditParameter(view, entry, EditOperation.MOVE))) {\n                        operationAllowed = true;\n\n                        Instant time = view.getInstantAt(evt);\n                        offsetDuration = Duration.between(entry.getStartAsZonedDateTime().toInstant(), time);\n                        entryDuration = entry.getDuration();\n\n                        LOGGER.finer(\"time at mouse pressed location: \" + time);\n                        LOGGER.finer(\"offset duration: \" + offsetDuration);\n                        LOGGER.finer(\"entry duration: \" + entryDuration);\n                    }\n                    break;\n                case END_TIME:\n                    if (entryEditPolicy.call(new EntryEditParameter(view, entry, EditOperation.CHANGE_END))) {\n                        operationAllowed = true;\n                    }\n                    break;\n                case START_TIME:\n                    if (entryEditPolicy.call(new EntryEditParameter(view, entry, EditOperation.CHANGE_START))) {\n                        operationAllowed = true;\n                    }\n                    break;\n                default:\n                    break;\n            }\n\n            if (!operationAllowed) {\n                return;\n            }\n\n            entryEditingAllowed = true;\n        }\n    }\n\n    private void mousePressedOnDayView(MouseEvent evt) {\n        if (view.isEditAvailability()) {\n            mousePressedEditAvailability(evt);\n        } else {\n            operation = Operation.CREATE_ENTRY;\n        }\n    }\n\n    private void mousePressedEditAvailability(MouseEvent evt) {\n        operation = Operation.EDIT_AVAILABILITY;\n        VirtualGrid availabilityGrid = view.getAvailabilityGrid();\n        setLassoStart(snapToGrid(view.getInstantAt(evt), availabilityGrid, false));\n        setLassoEnd(snapToGrid(view.getInstantAt(evt), availabilityGrid, false).plus(availabilityGrid.getAmount(), availabilityGrid.getUnit()));\n    }\n\n    private Entry<?> createEntryAt(MouseEvent evt) {\n        Optional<Calendar> calendar = view.getCalendarAt(evt.getX(), evt.getY());\n        Instant instantAt = view.getInstantAt(evt);\n\n        VirtualGrid virtualGrid = view.getVirtualGrid();\n        if (virtualGrid != null) {\n            instantAt = snapToGrid(instantAt, virtualGrid, false);\n        }\n\n        ZonedDateTime time = ZonedDateTime.ofInstant(instantAt, view.getZoneId());\n        Entry<?> newEntry = view.createEntryAt(time, calendar.orElse(null), false);\n        if (newEntry == null) {\n            return null;\n        }\n\n        Duration duration = newEntry.getMinimumDuration();\n\n        LOGGER.fine(\"minimum duration for the entry is \" + duration);\n\n        if (virtualGrid != null) {\n            LOGGER.fine(\"checking the virtual grid duration\");\n            Duration gridAmount = Duration.of(virtualGrid.getAmount(), virtualGrid.getUnit());\n            if (gridAmount.toMillis() > duration.toMillis()) {\n                LOGGER.fine(\"using the grid amount as it is longer than the minimum duration of the entry\");\n                duration = gridAmount;\n            }\n        }\n\n        newEntry.setInterval(newEntry.getInterval().withEndTime(newEntry.getInterval().getStartTime().plus(duration)));\n\n        view.getSelections().clear();\n        view.getSelections().add(newEntry);\n\n        return newEntry;\n    }\n\n    private void mouseDragged(MouseEvent evt) {\n        if (evt.isStillSincePress()) {\n            return;\n        }\n\n        if (evt.isSynthesized()) {\n            return;\n        }\n\n        switch (operation) {\n            case NONE:\n                break;\n            case EDIT_ENTRY:\n                if (entry != null) {\n                    Calendar calendar = entry.getCalendar();\n                    if (!calendar.isReadOnly()) {\n                        mouseDraggedEditEntry(evt);\n                    }\n                }\n                break;\n            case EDIT_AVAILABILITY:\n                mouseDraggedEditAvailability(evt);\n                break;\n            case CREATE_ENTRY:\n                mouseDraggedCreateEntry(evt);\n                break;\n        }\n    }\n\n    private void mouseDraggedEditAvailability(MouseEvent evt) {\n        setLassoEnd(snapToGrid(view.getInstantAt(evt), view.getAvailabilityGrid(), true));\n    }\n\n    private void mouseDraggedCreateEntry(MouseEvent evt) {\n        if (entry == null) {\n            dragMode = null;\n            handle = null;\n\n            // Important, use the initial mouse event when the user pressed the button\n            entry = createEntryAt(mousePressedEvent);\n            if (entry == null) {\n                return;\n            }\n\n            DayView dayView = null;\n\n            if (view instanceof DayView) {\n                dayView = (DayView) view;\n            } else if (view instanceof WeekView) {\n                WeekView weekView = (WeekView) view;\n                dayView = weekView.getWeekDayViews().get(0);\n            }\n\n            if (dayView != null) {\n                dragMode = DragMode.END_TIME;\n                handle = Handle.BOTTOM;\n            }\n\n            if (dayEntryView != null) {\n                dayEntryView.getProperties().put(\"dragged-end\", true);\n            }\n\n            DraggedEntry draggedEntry = new DraggedEntry(entry, dragMode);\n            draggedEntry.setOffsetDuration(offsetDuration);\n            view.setDraggedEntry(draggedEntry);\n        }\n\n        changeEndTime(evt);\n    }\n\n    private void mouseDraggedEditEntry(MouseEvent evt) {\n        if (entryEditingAllowed && view.getDraggedEntry() == null) {\n            DraggedEntry draggedEntry = new DraggedEntry(entry, dragMode);\n            draggedEntry.setOffsetDuration(offsetDuration);\n            view.setDraggedEntry(draggedEntry);\n\n            switch (dragMode) {\n                case START_AND_END_TIME:\n                    if (dayEntryView != null) {\n                        dayEntryView.getProperties().put(\"dragged\", true);\n                    }\n                    break;\n                case END_TIME:\n                    if (dayEntryView != null) {\n                        dayEntryView.getProperties().put(\"dragged-end\", true);\n                    }\n                    break;\n                case START_TIME:\n                    if (dayEntryView != null) {\n                        dayEntryView.getProperties().put(\"dragged-start\", true);\n                    }\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        switch (dragMode) {\n            case START_TIME:\n                switch (handle) {\n                    case TOP:\n                    case BOTTOM:\n                        changeStartTime(evt);\n                        break;\n                    case CENTER:\n                        break;\n                }\n                break;\n            case END_TIME:\n                switch (handle) {\n                    case TOP:\n                    case BOTTOM:\n                        changeEndTime(evt);\n                        break;\n                    case CENTER:\n                        break;\n                }\n                break;\n            case START_AND_END_TIME:\n                changeStartAndEndTime(evt);\n                break;\n        }\n    }\n\n    private void mouseReleased(MouseEvent evt) {\n        if (!evt.getButton().equals(MouseButton.PRIMARY) || evt.getClickCount() > 1) {\n            return;\n        }\n\n        switch (operation) {\n            case NONE:\n                break;\n            case EDIT_ENTRY:\n                mouseReleasedEditEntry();\n                break;\n            case EDIT_AVAILABILITY:\n                mouseReleasedEditAvailability();\n                break;\n            case CREATE_ENTRY:\n                mouseReleasedCreateEntry();\n                break;\n        }\n\n        operation = Operation.NONE;\n    }\n\n    private void mouseReleasedEditAvailability() {\n        getOnLassoFinished().accept(getLassoStart(), getLassoEnd());\n        setLassoStart(null);\n        setLassoEnd(null);\n    }\n\n    private void mouseReleasedEditEntry() {\n        if (dayEntryView != null) {\n            dayEntryView.getProperties().put(\"dragged\", false);\n            dayEntryView.getProperties().put(\"dragged-start\", false);\n            dayEntryView.getProperties().put(\"dragged-end\", false);\n        }\n\n        DraggedEntry draggedEntry = view.getDraggedEntry();\n\n        if (draggedEntry != null) {\n            view.setDraggedEntry(null);\n\n            Interval newInterval = draggedEntry.getInterval();\n\n//            if (entry.isRecurrence()) {\n//                Entry sourceEntry = entry.getRecurrenceSourceEntry();\n//                Interval sourceInterval = sourceEntry.getInterval();\n//\n//                sourceInterval = sourceInterval.withStartTime(newInterval.getStartTime());\n//                sourceInterval = sourceInterval.withDuration(newInterval.getDuration());\n//\n//                sourceEntry.setInterval(sourceInterval);\n//            } else {\n            entry.setInterval(newInterval);\n//            }\n\n            if (view.isShowDetailsUponEntryCreation() && operation.equals(Operation.CREATE_ENTRY)) {\n                view.fireEvent(new RequestEvent(view, view, entry));\n            }\n        }\n    }\n\n    private void mouseReleasedCreateEntry() {\n        if (entry != null) {\n            Calendar calendar = entry.getCalendar();\n            if (calendar.isReadOnly()) {\n                return;\n            }\n\n            if (dayEntryView != null) {\n                dayEntryView.getProperties().put(\"dragged\", false);\n                dayEntryView.getProperties().put(\"dragged-start\", false);\n                dayEntryView.getProperties().put(\"dragged-end\", false);\n            }\n\n            /*\n             * We might run in the sampler application. Then the entry view will not\n             * be inside a date control.\n             */\n            DraggedEntry draggedEntry = view.getDraggedEntry();\n\n            if (draggedEntry != null) {\n                view.setDraggedEntry(null);\n                entry.setInterval(draggedEntry.getInterval());\n                if (view.isShowDetailsUponEntryCreation() && operation.equals(Operation.CREATE_ENTRY)) {\n                    view.fireEvent(new RequestEvent(view, view, entry));\n                }\n            }\n        }\n    }\n\n    private void changeStartTime(MouseEvent evt) {\n        DraggedEntry draggedEntry = view.getDraggedEntry();\n\n        Instant gridTime = fixTimeIfOutsideView(evt, snapToGrid(view.getInstantAt(evt), view.getVirtualGrid(), true));\n\n        LOGGER.finer(\"changing start time, time = \" + gridTime);\n\n        if (isMinimumDuration(entry, entry.getEndAsZonedDateTime().toInstant(), gridTime)) {\n\n            LocalDate startDate;\n            LocalDate endDate;\n\n            LocalTime startTime;\n            LocalTime endTime;\n\n            ZonedDateTime gridZonedTime = ZonedDateTime.ofInstant(gridTime, draggedEntry.getZoneId());\n\n            if (gridTime.isAfter(entry.getEndAsZonedDateTime().toInstant())) {\n                if (view.isEnableStartAndEndTimesFlip()) {\n                    startTime = entry.getEndTime();\n                    startDate = entry.getEndDate();\n                    endTime = gridZonedTime.toLocalTime();\n                    endDate = gridZonedTime.toLocalDate();\n                } else {\n                    startTime = entry.getEndTime().minus(entry.getMinimumDuration());\n                    startDate = entry.getEndDate();\n                    endTime = entry.getEndTime();\n                    endDate = entry.getEndDate();\n                }\n            } else {\n                startDate = gridZonedTime.toLocalDate();\n                startTime = gridZonedTime.toLocalTime();\n                endTime = entry.getEndTime();\n                endDate = entry.getEndDate();\n            }\n\n            LOGGER.finer(\"new interval: sd = \" + startDate + \", st = \" + startTime + \", ed = \" + endDate + \", et = \" + endTime);\n\n            draggedEntry.setInterval(startDate, startTime, endDate, endTime);\n        }\n    }\n\n    private void changeEndTime(MouseEvent evt) {\n        DraggedEntry draggedEntry = view.getDraggedEntry();\n\n        Instant gridTime = fixTimeIfOutsideView(evt, snapToGrid(view.getInstantAt(evt), view.getVirtualGrid(), true));\n\n        LOGGER.finer(\"changing end time, time = \" + gridTime);\n\n        if (isMinimumDuration(entry, entry.getStartAsZonedDateTime().toInstant(), gridTime)) {\n\n            LOGGER.finer(\"dragged entry: \" + draggedEntry.getInterval());\n\n            LocalDate startDate;\n            LocalDate endDate;\n\n            LocalTime startTime;\n            LocalTime endTime;\n\n            ZonedDateTime gridZonedTime = ZonedDateTime.ofInstant(gridTime, draggedEntry.getZoneId());\n\n            if (gridTime.isBefore(entry.getStartAsZonedDateTime().toInstant())) {\n                if (view.isEnableStartAndEndTimesFlip()) {\n                    endTime = entry.getStartTime();\n                    endDate = entry.getStartDate();\n                    startTime = gridZonedTime.toLocalTime();\n                    startDate = gridZonedTime.toLocalDate();\n                } else {\n                    startTime = entry.getStartTime();\n                    startDate = entry.getStartDate();\n                    endTime = entry.getStartTime().plus(entry.getMinimumDuration());\n                    endDate = entry.getStartDate();\n                }\n            } else {\n                startTime = entry.getStartTime();\n                startDate = entry.getStartDate();\n                endTime = gridZonedTime.toLocalTime();\n                endDate = gridZonedTime.toLocalDate();\n            }\n\n            LOGGER.finer(\"new interval: sd = \" + startDate + \", st = \" + startTime + \", ed = \" + endDate + \", et = \" + endTime);\n\n            draggedEntry.setInterval(startDate, startTime, endDate, endTime);\n        }\n    }\n\n    private void changeStartAndEndTime(MouseEvent evt) {\n        DraggedEntry draggedEntry = view.getDraggedEntry();\n\n        Instant locationTime = fixTimeIfOutsideView(evt, view.getInstantAt(evt));\n\n        LOGGER.fine(\"changing start/end time, time = \" + locationTime + \" offset duration = \" + offsetDuration);\n\n        if (locationTime != null && offsetDuration != null) {\n\n            Instant newStartTime = locationTime.minus(offsetDuration);\n            LOGGER.fine(\"new start time = \" + newStartTime);\n\n            newStartTime = snapToGrid(newStartTime, view.getVirtualGrid(), true);\n            Instant newEndTime = newStartTime.plus(entryDuration);\n\n            LOGGER.fine(\"new start time (grid) = \" + newStartTime);\n            LOGGER.fine(\"new end time = \" + newEndTime);\n\n            ZonedDateTime gridStartZonedTime = ZonedDateTime.ofInstant(newStartTime, draggedEntry.getZoneId());\n            ZonedDateTime gridEndZonedTime = ZonedDateTime.ofInstant(newEndTime, draggedEntry.getZoneId());\n\n            LocalDate startDate = gridStartZonedTime.toLocalDate();\n            LocalTime startTime = gridStartZonedTime.toLocalTime();\n\n            LocalDate endDate = LocalDateTime.of(startDate, startTime).plus(entryDuration).toLocalDate();\n            LocalTime endTime = gridEndZonedTime.toLocalTime();\n\n            draggedEntry.setInterval(startDate, startTime, endDate, endTime);\n        }\n    }\n\n    private Instant fixTimeIfOutsideView(MouseEvent evt, Instant gridTime) {\n        /*\n         * Fix the time calculation if the mouse cursor exits the day view area.\n         * Note: day view can also be a WeekView as it extends DayViewBase.\n         */\n        if (evt.getX() > view.getWidth() || evt.getX() < 0) {\n            ZonedDateTime zdt = ZonedDateTime.ofInstant(gridTime, entry.getZoneId());\n            gridTime = ZonedDateTime.of(entry.getStartDate(), zdt.toLocalTime(), zdt.getZone()).toInstant();\n        }\n        return gridTime;\n    }\n\n    private boolean isMinimumDuration(Entry<?> entry, Instant\n            timeA, Instant timeB) {\n        Duration minDuration = entry.getMinimumDuration().abs();\n        if (minDuration != null) {\n            Duration duration = Duration.between(timeA, timeB).abs();\n            return !duration.minus(minDuration).isNegative();\n        }\n\n        return true;\n    }\n\n    private Instant snapToGrid(Instant time, VirtualGrid grid,\n                               boolean checkCloser) {\n        if (grid == null) {\n            return time;\n        }\n\n        DayOfWeek firstDayOfWeek = view.getFirstDayOfWeek();\n        Instant lowerTime = grid.adjustTime(time, view.getZoneId(), false, firstDayOfWeek);\n\n        if (checkCloser) {\n            Instant upperTime = grid.adjustTime(time, view.getZoneId(), true, firstDayOfWeek);\n            if (Duration.between(time, upperTime).abs().minus(Duration.between(time, lowerTime).abs()).isNegative()) {\n                return upperTime;\n            }\n        }\n\n        return lowerTime;\n    }\n\n    private final ObjectProperty<Instant> lassoStart = new SimpleObjectProperty<>(this, \"lassoStart\");\n\n    public final Instant getLassoStart() {\n        return lassoStart.get();\n    }\n\n    public final ObjectProperty<Instant> lassoStartProperty() {\n        return lassoStart;\n    }\n\n    public final void setLassoStart(Instant lassoStart) {\n        this.lassoStart.set(lassoStart);\n    }\n\n    private final ObjectProperty<Instant> lassoEnd = new SimpleObjectProperty<>(this, \"lassoEnd\");\n\n    public final Instant getLassoEnd() {\n        return lassoEnd.get();\n    }\n\n    public final ObjectProperty<Instant> lassoEndProperty() {\n        return lassoEnd;\n    }\n\n    public final void setLassoEnd(Instant lassoEnd) {\n        this.lassoEnd.set(lassoEnd);\n    }\n\n    private final ObjectProperty<BiConsumer<Instant, Instant>> onLassoFinished = new SimpleObjectProperty<>(this, \"onLassoFinished\", (start, end) -> {\n    });\n\n    public final BiConsumer<Instant, Instant> getOnLassoFinished() {\n        return onLassoFinished.get();\n    }\n\n    public final ObjectProperty<BiConsumer<Instant, Instant>> onLassoFinishedProperty\n            () {\n        return onLassoFinished;\n    }\n\n    public final void setOnLassoFinished\n            (BiConsumer<Instant, Instant> onLassoFinished) {\n        this.onLassoFinished.set(onLassoFinished);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewScrollPane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.util.ViewHelper;\nimport com.calendarfx.view.DayViewBase;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Orientation;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.input.DragEvent;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.input.ScrollEvent;\nimport javafx.scene.input.TransferMode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.shape.Rectangle;\n\nimport java.time.LocalTime;\nimport java.util.Objects;\n\n/**\n * A specialized scrollpane used for automatic scrolling when the user performs\n * a drag operation close to the edges of the pane.\n */\npublic class DayViewScrollPane extends Pane {\n\n    private final DayViewBase dayView;\n    private final ScrollBar scrollBar;\n\n    private LocalTime cachedStartTime;\n\n    private final InvalidationListener scrollBarListener = it -> getDayView().setTranslateY(getScrollBar().getValue() * -1);\n\n\n    private final InvalidationListener translateYListener = it -> {\n        updateVisibleTimeRange(\"translate-y changed\");\n        cachedStartTime = getStartTime();\n        getScrollBar().setValue(-getDayView().getTranslateY());\n    };\n\n    private final InvalidationListener sceneChangedListener = it -> updateVisibleTimeRange(\"scene changed\");\n    private final InvalidationListener scrollPaneHeightChangedListener = it -> updateVisibleTimeRange(\"height of scrollpane changed\");\n    private final InvalidationListener dayViewHeightChangedListener = it -> updateVisibleTimeRange(\"height of day view changed\");\n    private final InvalidationListener visibleHoursChangedListener = it -> updateVisibleTimeRange(\"visible hours changed\");\n    private final InvalidationListener layoutListener = it -> requestLayout();\n\n    private final WeakInvalidationListener weakScrollBarListener = new WeakInvalidationListener(scrollBarListener);\n    private final WeakInvalidationListener weakTranslateYListener = new WeakInvalidationListener(translateYListener);\n    private final WeakInvalidationListener weakSceneChangedListener = new WeakInvalidationListener(sceneChangedListener);\n    private final WeakInvalidationListener weakScrollPaneHeightChangedListener = new WeakInvalidationListener(scrollPaneHeightChangedListener);\n    private final WeakInvalidationListener weakDayViewHeightChangedListener = new WeakInvalidationListener(dayViewHeightChangedListener);\n    private final WeakInvalidationListener weakVisibleHoursChangedListener = new WeakInvalidationListener(visibleHoursChangedListener);\n    private final WeakInvalidationListener weakLayoutListener = new WeakInvalidationListener(layoutListener);\n\n    /**\n     * Constructs a new scrollpane for the given day view.\n     *\n     * @param dayView the content node\n     * @param scrollBar the scrollbar used for vertical scrolling\n     */\n    public DayViewScrollPane(DayViewBase dayView, ScrollBar scrollBar) {\n        super();\n\n        this.dayView = Objects.requireNonNull(dayView);\n        this.dayView.setManaged(false);\n        this.dayView.layoutBoundsProperty().addListener(weakLayoutListener);\n\n        this.scrollBar = Objects.requireNonNull(scrollBar);\n\n        scrollBar.setOrientation(Orientation.VERTICAL);\n        scrollBar.maxProperty().bind(dayView.heightProperty().subtract(heightProperty()));\n        scrollBar.visibleAmountProperty().bind(Bindings.multiply(scrollBar.maxProperty(), Bindings.divide(heightProperty(), dayView.heightProperty())));\n        scrollBar.valueProperty().addListener(weakScrollBarListener);\n\n        // user clicks on scrollbar arrows -> scroll one hour\n        scrollBar.unitIncrementProperty().bind(dayView.hourHeightProperty());\n\n        // user clicks in background of scrollbar = block scroll -> scroll half a page\n        scrollBar.blockIncrementProperty().bind(heightProperty().divide(2));\n\n        dayView.translateYProperty().addListener(weakTranslateYListener);\n\n        getChildren().add(dayView);\n\n        heightProperty().addListener(weakScrollPaneHeightChangedListener);\n\n        dayView.sceneProperty().addListener(weakSceneChangedListener);\n        dayView.heightProperty().addListener(weakDayViewHeightChangedListener);\n        dayView.visibleHoursProperty().addListener(weakVisibleHoursChangedListener);\n\n        dayView.earlyLateHoursStrategyProperty().addListener(weakLayoutListener);\n        dayView.hourHeightCompressedProperty().addListener(weakLayoutListener);\n        dayView.hoursLayoutStrategyProperty().addListener(weakLayoutListener);\n        dayView.hourHeightProperty().addListener(weakLayoutListener);\n\n        updateVisibleTimeRange(\"initial call\");\n\n        addEventFilter(ScrollEvent.SCROLL, evt -> {\n            scrollY(evt.getDeltaY());\n            evt.consume();\n        });\n\n        // regular drag, e.g. of an entry view\n        addEventFilter(MouseEvent.MOUSE_DRAGGED, this::autoscrollIfNeeded);\n        addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> stopAutoScrollIfNeeded());\n\n        // drag and drop from the outside\n        // TODO: PUT BACK IN addEventFilter(MouseEvent.DRAG_DETECTED, evt -> startDrag(evt));\n        addEventFilter(DragEvent.DRAG_OVER, this::autoscrollIfNeeded);\n        addEventFilter(DragEvent.DRAG_EXITED, evt -> stopAutoScrollIfNeeded());\n        addEventFilter(DragEvent.DRAG_DROPPED, evt -> stopAutoScrollIfNeeded());\n        addEventFilter(DragEvent.DRAG_DONE, evt -> stopAutoScrollIfNeeded());\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(widthProperty());\n        clip.heightProperty().bind(heightProperty());\n        setClip(clip);\n    }\n\n    private void scrollY(double deltaY) {\n        Insets insets = getInsets();\n        dayView.setTranslateY(Math.min(0, Math.max(dayView.getTranslateY() + deltaY, getMaxTranslateY(insets))));\n    }\n\n    public void scrollToTime(LocalTime time) {\n        double y = ViewHelper.getTimeLocation(dayView, time, true);\n        Insets insets = getInsets();\n\n        // only scroll if the given time is not in the visible range\n        if (y < Math.abs(dayView.getTranslateY()) || y > Math.abs(dayView.getTranslateY()) + getHeight()) {\n            // place the given time at one third of the visible height\n            dayView.setTranslateY(Math.min(0, Math.max(-y + getHeight() / 3, getMaxTranslateY(insets))));\n        }\n    }\n\n    private void updateVisibleTimeRange(String reason) {\n        LoggingDomain.VIEW.fine(\"reason for updating visible time range: \" + reason);\n\n        if (dayView.getScene() == null || dayView.getHeight() == 0) {\n            return;\n        }\n\n        final LocalTime startTime = dayView.getZonedDateTimeAt(0, -dayView.getTranslateY()).toLocalTime();\n        final LocalTime endTime = dayView.getZonedDateTimeAt(0, -dayView.getTranslateY() + getHeight()).toLocalTime();\n\n        this.startTime.set(startTime);\n        this.endTime.set(endTime);\n    }\n\n    // visible start time support\n\n    private final ReadOnlyObjectWrapper<LocalTime> startTime = new ReadOnlyObjectWrapper<>(this, \"startTime\", LocalTime.MIN);\n\n    public final ReadOnlyObjectProperty<LocalTime> startTimeProperty() {\n        return startTime.getReadOnlyProperty();\n    }\n\n    public final LocalTime getStartTime() {\n        return startTime.get();\n    }\n\n    // visible end time support\n\n    private final ReadOnlyObjectWrapper<LocalTime> endTime = new ReadOnlyObjectWrapper<>(this, \"endTime\", LocalTime.MIN);\n\n    public final ReadOnlyObjectProperty<LocalTime> endTimeProperty() {\n        return endTime.getReadOnlyProperty();\n    }\n\n    public final LocalTime getEndTime() {\n        return endTime.get();\n    }\n\n    public final DayViewBase getDayView() {\n        return dayView;\n    }\n\n    public final ScrollBar getScrollBar() {\n        return scrollBar;\n    }\n\n    @Override\n    protected double computePrefWidth(double height) {\n        return getDayView().prefWidth(-1);\n    }\n\n    @Override\n    protected void layoutChildren() {\n        super.layoutChildren();\n\n        switch (dayView.getHoursLayoutStrategy()) {\n            case FIXED_HOUR_COUNT:\n                double height = getHeight();\n                int visibleHours = dayView.getVisibleHours();\n                dayView.setHourHeight(Math.max(1, height / visibleHours)); // height must be at least 1px\n                break;\n            case FIXED_HOUR_HEIGHT:\n                break;\n        }\n\n        Insets insets = getInsets();\n\n        final double ph = dayView.prefHeight(-1);\n        dayView.resizeRelocate(\n                snapPositionX(insets.getLeft()),\n                snapPositionY(insets.getTop()),\n                snapSizeX(getWidth() - insets.getLeft() - insets.getRight()),\n                snapSizeY(Math.max(ph, getHeight() - insets.getTop() - insets.getBottom())));\n\n        switch (dayView.getHoursLayoutStrategy()) {\n            case FIXED_HOUR_COUNT:\n                if (cachedStartTime != null) {\n                    dayView.setTranslateY(-ViewHelper.getTimeLocation(dayView, cachedStartTime, true));\n                }\n                break;\n            case FIXED_HOUR_HEIGHT:\n                break;\n        }\n\n        if (dayView.getTranslateY() + dayView.getHeight() < getHeight() - insets.getTop() - insets.getBottom()) {\n            dayView.setTranslateY(getMaxTranslateY(insets));\n        }\n    }\n\n    private double getMaxTranslateY(Insets insets) {\n        return (getHeight() - insets.getTop() - insets.getBottom()) - dayView.getHeight();\n    }\n\n    private void autoscrollIfNeeded(DragEvent evt) {\n        evt.acceptTransferModes(TransferMode.ANY);\n\n        if (getBoundsInLocal().getWidth() < 1) {\n            if (getBoundsInLocal().getWidth() < 1) {\n                stopAutoScrollIfNeeded();\n                return;\n            }\n        }\n\n        double yOffset = 0;\n\n        // y offset\n\n        double delta = evt.getSceneY() - localToScene(0, 0).getY();\n        double proximity = 20;\n        if (delta < proximity) {\n            yOffset = -(proximity - delta);\n        }\n\n        delta = localToScene(0, 0).getY() + getHeight() - evt.getSceneY();\n        if (delta < proximity) {\n            yOffset = proximity - delta;\n        }\n\n        if (yOffset != 0) {\n            autoscroll(yOffset);\n        } else {\n            stopAutoScrollIfNeeded();\n        }\n    }\n\n    private void autoscrollIfNeeded(MouseEvent evt) {\n        if (getBoundsInLocal().getWidth() < 1) {\n            if (getBoundsInLocal().getWidth() < 1) {\n                stopAutoScrollIfNeeded();\n                return;\n            }\n        }\n\n        double yOffset = 0;\n\n        // y offset\n\n        double delta = evt.getSceneY() - localToScene(0, 0).getY();\n        if (delta < 0) {\n            yOffset = Math.max(delta / 2, -10);\n        }\n\n        delta = localToScene(0, 0).getY() + getHeight() - evt.getSceneY();\n        if (delta < 0) {\n            yOffset = Math.min(-delta / 2, 10);\n        }\n\n        if (yOffset != 0) {\n            autoscroll(yOffset);\n        } else {\n            stopAutoScrollIfNeeded();\n        }\n    }\n\n    class ScrollThread extends Thread {\n        private boolean running = true;\n        private double yOffset;\n\n        public ScrollThread() {\n            super(\"Autoscrolling ScrollPane\");\n            setDaemon(true);\n        }\n\n        @Override\n        public void run() {\n\n            /*\n             * Some initial delay, especially useful when dragging something in\n             * from the outside.\n             */\n\n            try {\n                Thread.sleep(300);\n            } catch (InterruptedException e1) {\n                e1.printStackTrace();\n            }\n\n            while (running) {\n\n                Platform.runLater(this::scrollToY);\n\n                try {\n                    sleep(15);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private void scrollToY() {\n            scrollY(-yOffset);\n        }\n\n        public void stopRunning() {\n            this.running = false;\n        }\n\n        public void setDelta(double yOffset) {\n            this.yOffset = yOffset;\n        }\n    }\n\n    private ScrollThread scrollThread;\n\n    private void autoscroll(double yOffset) {\n        if (scrollThread == null) {\n            scrollThread = new ScrollThread();\n            scrollThread.start();\n        }\n\n        scrollThread.setDelta(yOffset);\n    }\n\n    private void stopAutoScrollIfNeeded() {\n        if (scrollThread != null) {\n            scrollThread.stopRunning();\n            scrollThread = null;\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.util.ViewHelper;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DateControl.Layer;\nimport com.calendarfx.view.DayEntryView;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DayViewBase.AvailabilityEditingEntryBehaviour;\nimport com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy;\nimport com.calendarfx.view.DayViewBase.GridType;\nimport com.calendarfx.view.DayViewBase.OverlapResolutionStrategy;\nimport com.calendarfx.view.DraggedEntry;\nimport com.calendarfx.view.EntryViewBase;\nimport com.calendarfx.view.EntryViewBase.AlignmentStrategy;\nimport com.calendarfx.view.EntryViewBase.HeightLayoutStrategy;\nimport com.calendarfx.view.EntryViewBase.Position;\nimport com.calendarfx.view.VirtualGrid;\nimport impl.com.calendarfx.view.util.Placement;\nimport impl.com.calendarfx.view.util.TimeBoundsResolver;\nimport impl.com.calendarfx.view.util.Util;\nimport impl.com.calendarfx.view.util.VisualBoundsResolver;\nimport javafx.animation.FadeTransition;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.value.ChangeListener;\nimport javafx.event.EventHandler;\nimport javafx.event.WeakEventHandler;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.control.Control;\nimport javafx.scene.layout.Region;\nimport javafx.scene.shape.Circle;\nimport javafx.scene.shape.Line;\nimport javafx.scene.shape.Rectangle;\nimport javafx.util.Callback;\nimport javafx.util.Duration;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.stream.Collectors;\n\n@SuppressWarnings(\"javadoc\")\npublic class DayViewSkin<T extends DayView> extends DayViewBaseSkin<T> implements LoadDataSettingsProvider {\n\n    private static final String MIDNIGHT_LINE_STYLE_CLASS = \"midnight-line\";\n    private static final String NOON_LINE_STYLE_CLASS = \"noon-line\";\n    private static final String HALF_HOUR_LINE_STYLE_CLASS = \"half-hour-line\";\n    private static final String FULL_HOUR_LINE_STYLE_CLASS = \"full-hour-line\";\n\n    private final List<Line> lines = new ArrayList<>();\n\n    private final DataLoader dataLoader = new DataLoader(this);\n\n    private final Circle currentTimeCircle;\n\n    private final Line currentTimeLine;\n\n    private DayEntryView draggedEntryView;\n\n    private final Region earlyHoursRegion;\n\n    private final Region lateHoursRegion;\n\n    private LocalDate displayedDate;\n\n    private double startY;\n\n    private final BackgroundCanvas backgroundCanvas = new BackgroundCanvas();\n\n    private final Group entryViewGroup = new Group();\n\n    public DayViewSkin(T view) {\n        super(view);\n\n        entryViewGroup.setMouseTransparent(false);\n        entryViewGroup.setManaged(false);\n        entryViewGroup.opacityProperty().bind(Bindings.createDoubleBinding(() -> view.isEditAvailability() && view.getEntryViewAvailabilityEditingBehaviour().equals(AvailabilityEditingEntryBehaviour.OPACITY) ? view.getEntryViewAvailabilityEditingOpacity() : 1,\n                view.editAvailabilityProperty(), view.entryViewAvailabilityEditingBehaviourProperty(), view.entryViewAvailabilityEditingOpacityProperty()));\n\n        earlyHoursRegion = new Region();\n        earlyHoursRegion.setMouseTransparent(true);\n        earlyHoursRegion.getStyleClass().add(\"early-hours-region\");\n        earlyHoursRegion.setManaged(false);\n        getChildren().add(earlyHoursRegion);\n\n        lateHoursRegion = new Region();\n        lateHoursRegion.setMouseTransparent(true);\n        lateHoursRegion.getStyleClass().add(\"late-hours-region\");\n        lateHoursRegion.setManaged(false);\n        getChildren().add(lateHoursRegion);\n\n        InvalidationListener drawBackgroundCanvasListener = it -> backgroundCanvas.draw();\n        view.editAvailabilityProperty().addListener(drawBackgroundCanvasListener);\n\n        getChildren().add(backgroundCanvas);\n\n        if (!view.isScrollingEnabled()) {\n            // Static lines use different styling for early / late hours, we do not want that\n            // when scrolling is enabled. In that case all lines need to look the same.\n            createStaticLines();\n        }\n\n        getChildren().add(entryViewGroup);\n\n        currentTimeCircle = new Circle(4);\n        currentTimeCircle.getStyleClass().add(\"current-time-circle\");\n        currentTimeCircle.setManaged(false);\n        currentTimeCircle.setMouseTransparent(true);\n        currentTimeCircle.setOpacity(0);\n        currentTimeCircle.visibleProperty().bind(view.enableCurrentTimeCircleProperty().and(view.editAvailabilityProperty().not()));\n        getChildren().add(currentTimeCircle);\n\n        currentTimeLine = new Line();\n        currentTimeLine.getStyleClass().add(\"current-time-line\");\n        currentTimeLine.setManaged(false);\n        currentTimeLine.setMouseTransparent(true);\n        currentTimeLine.setOpacity(0);\n        currentTimeLine.visibleProperty().bind(view.enableCurrentTimeMarkerProperty().and(view.editAvailabilityProperty().not()));\n        getChildren().add(currentTimeLine);\n\n        view.scrollTimeProperty().addListener(drawBackgroundCanvasListener);\n        view.lassoStartProperty().addListener(drawBackgroundCanvasListener);\n        view.lassoEndProperty().addListener(drawBackgroundCanvasListener);\n        view.editAvailabilityProperty().addListener(drawBackgroundCanvasListener);\n\n        view.availabilityCalendarProperty().addListener((obs, oldCalendar, newCalendar) -> listenToAvailabilityCalendar(oldCalendar, newCalendar));\n        listenToAvailabilityCalendar(null, view.getAvailabilityCalendar());\n\n        if (!(this instanceof WeekDayViewSkin)) {\n            /*\n             * Dragging inside week day views will be handled by a drag controller\n             * installed on the week view, not on the individual days.\n             */\n            new DayViewEditController(view);\n        }\n\n        setupCurrentTimeMarkerSupport();\n\n        view.draggedEntryProperty().addListener(it -> addOrRemoveDraggedEntryView());\n\n        view.showCurrentTimeMarkerProperty().addListener(it -> updateTimelineVisibility());\n        view.showCurrentTimeTodayMarkerProperty().addListener(it -> updateTimelineVisibility());\n\n        view.layoutProperty().addListener(it -> view.requestLayout());\n        view.visibleLayersProperty().addListener((InvalidationListener) it -> view.requestLayout());\n\n        // infinite scrolling\n        view.scrollTimeProperty().addListener(it -> {\n            loadData(\"view scrolled\");\n            view.requestLayout();\n        });\n\n        view.scrollingEnabledProperty().addListener(it -> {\n            if (view.isScrollingEnabled()) {\n                lines.clear();\n            } else {\n                createStaticLines();\n            }\n            view.requestLayout();\n        });\n\n        updateShowMarkers();\n        updateTimelineVisibility();\n\n        view.dateProperty().addListener(it -> {\n            if (displayedDate == null || !displayedDate.equals(view.getDate())) {\n                loadData(\"date changed\");\n            }\n        });\n\n        view.suspendUpdatesProperty().addListener(evt -> loadData(\"suspend updates was changed to \" + view.isSuspendUpdates()));\n        view.getCalendars().addListener((Observable obs) -> loadData(\"list of calendars changed\"));\n\n        final InvalidationListener styleLinesListener = it -> updateLineStyling();\n        view.startTimeProperty().addListener(styleLinesListener);\n        view.endTimeProperty().addListener(styleLinesListener);\n        view.earlyLateHoursStrategyProperty().addListener(styleLinesListener);\n        view.gridTypeProperty().addListener(styleLinesListener);\n\n        loadData(\"initial data loading\");\n\n        InvalidationListener loadDataListener = it -> {\n            if (view.isScrollingEnabled()) {\n                // run later, or we cause flickering\n                Platform.runLater(() -> {\n                    loadData(\"height changed\");\n                });\n            }\n        };\n        view.heightProperty().addListener(loadDataListener);\n        view.hourHeightProperty().addListener(loadDataListener);\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(view.widthProperty());\n        clip.heightProperty().bind(view.heightProperty());\n        view.setClip(clip);\n\n        getSkinnable().setOnMousePressed(evt -> startY = evt.getScreenY());\n\n        getSkinnable().setOnMouseDragged(evt -> {\n            if (view.isScrollingEnabled()) {\n                view.setScrollTime(view.getZonedDateTimeAt(0, startY - evt.getScreenY(), view.getZoneId()));\n                startY = evt.getScreenY();\n            }\n        });\n\n        view.gridLinesProperty().addListener(drawBackgroundCanvasListener);\n        view.gridLineColorProperty().addListener(drawBackgroundCanvasListener);\n        view.gridTypeProperty().addListener(drawBackgroundCanvasListener);\n    }\n\n    private final EventHandler<CalendarEvent> availabilityHandler = evt -> backgroundCanvas.draw();\n\n    private final WeakEventHandler<CalendarEvent> weakAvailabilityHandler = new WeakEventHandler<>(availabilityHandler);\n\n    private void listenToAvailabilityCalendar(Calendar oldCalendar, Calendar newCalendar) {\n        if (oldCalendar != null) {\n            oldCalendar.removeEventHandler(weakAvailabilityHandler);\n        }\n\n        if (newCalendar != null) {\n            newCalendar.addEventHandler(weakAvailabilityHandler);\n        }\n    }\n\n    private void createStaticLines() {\n        lines.clear();\n\n        for (int i = 1; i < 24; i++) {\n            createLine(HALF_HOUR_LINE_STYLE_CLASS);\n            createLine(FULL_HOUR_LINE_STYLE_CLASS);\n        }\n\n        createLine(HALF_HOUR_LINE_STYLE_CLASS);\n\n        updateLineStyling();\n    }\n\n    @Override\n    protected void calendarVisibilityChanged() {\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void refreshData() {\n        loadData(\"refreshData() was called\");\n    }\n\n    private void updateTimelineVisibility() {\n        double lineOpacity = getSkinnable().isShowCurrentTimeMarker() ? 1 : 0;\n        FadeTransition lineTransition = new FadeTransition(Duration.millis(600), currentTimeLine);\n        lineTransition.setToValue(lineOpacity);\n        lineTransition.play();\n\n        double circleOpacity = getSkinnable().isShowCurrentTimeTodayMarker() ? 1 : 0;\n        FadeTransition circleTransition = new FadeTransition(Duration.millis(600), currentTimeCircle);\n        circleTransition.setToValue(circleOpacity);\n        circleTransition.play();\n    }\n\n    private void setupCurrentTimeMarkerSupport() {\n        T view = getSkinnable();\n\n        // do not use an invalidation listener as that will also fire for same dates\n        ChangeListener listener = (obs, oldValue, newValue) -> updateShowMarkers();\n        view.dateProperty().addListener(listener);\n        view.todayProperty().addListener(listener);\n    }\n\n    private void updateShowMarkers() {\n        T view = getSkinnable();\n        view.getProperties().put(\"show.current.time.marker\", isShowingTimeMarker());\n        view.getProperties().put(\"show.current.time.today.marker\", isShowingTimeTodayMarker());\n    }\n\n    protected boolean isShowingTimeMarker() {\n        return getSkinnable().getDate().equals(getSkinnable().getToday());\n    }\n\n    protected boolean isShowingTimeTodayMarker() {\n        return isShowingTimeMarker();\n    }\n\n    private final InvalidationListener layoutListener = it -> getSkinnable().requestLayout();\n\n    private final WeakInvalidationListener weakLayoutListener = new WeakInvalidationListener(layoutListener);\n\n    private void addOrRemoveDraggedEntryView() {\n        DayView view = getSkinnable();\n        DraggedEntry draggedEntry = view.getDraggedEntry();\n        if (draggedEntry != null) {\n            draggedEntryView = doAddEntryView(draggedEntry);\n            draggedEntryView.toFront();\n            draggedEntryView.setMouseTransparent(true);\n            draggedEntryView.getProperties().put(\"selected\", true);\n            draggedEntry.intervalProperty().addListener(weakLayoutListener);\n        } else {\n            if (draggedEntryView != null) {\n                removeEntryView(draggedEntryView.getEntry(), null);\n                draggedEntryView = null;\n            }\n        }\n\n        view.requestLayout();\n    }\n\n    private void loadData(String reason) {\n        if (getSkinnable().isSuspendUpdates()) {\n            return;\n        }\n        updateEntries(reason);\n    }\n\n    private void createLine() {\n        createLine(null);\n    }\n\n    private void createLine(String styleClass) {\n        Line line = new Line();\n        line.setManaged(false);\n        line.setMouseTransparent(true);\n        if (styleClass != null) {\n            line.getStyleClass().add(styleClass);\n        }\n        lines.add(line);\n        getChildren().add(line);\n    }\n\n    private void updateLineStyling() {\n        T dayView = getSkinnable();\n\n        LocalTime startTime = dayView.getStartTime();\n        LocalTime endTime = dayView.getEndTime();\n\n        boolean showEarlyHoursRegion = startTime.isAfter(LocalTime.MIN);\n        boolean showLateHoursRegion = endTime.isBefore(LocalTime.MAX);\n\n        earlyHoursRegion.setVisible(showEarlyHoursRegion);\n        lateHoursRegion.setVisible(showLateHoursRegion);\n\n        int lineCount = lines.size();\n\n        for (int i = 0; i < lineCount; i++) {\n            Line line = lines.get(i);\n\n            line.getStyleClass().removeAll(\"early-hour-line\", \"late-hour-line\");\n\n            int hour = (i + 1) / 2;\n            int minute = 0;\n\n            boolean halfHourLine = (i % 2 == 0);\n            if (halfHourLine) {\n                minute = 30;\n            }\n\n            LocalTime time = LocalTime.of(hour, minute);\n\n            if (time.isBefore(startTime)) {\n                if (!line.getStyleClass().contains(\"early-hour-line\")) {\n                    line.getStyleClass().add(\"early-hour-line\");\n                }\n            }\n            if (time.isAfter(endTime)) {\n                if (!line.getStyleClass().contains(\"late-hour-line\")) {\n                    line.getStyleClass().add(\"late-hour-line\");\n                }\n            }\n\n            if (dayView.getGridType().equals(GridType.STANDARD)) {\n                switch (dayView.getEarlyLateHoursStrategy()) {\n                    case HIDE:\n                        /*\n                         * We do not show ... a) lines before the start time and after\n                         * the end time b) lines directly on the start time or end time\n                         * because they make the UI look messy\n                         */\n                        line.setVisible(!time.isBefore(startTime) && !time.equals(startTime) && !time.isAfter(endTime) && !time.equals(endTime));\n                        break;\n                    case SHOW:\n                        line.setVisible(true);\n                        break;\n                    case SHOW_COMPRESSED:\n                        line.setVisible(!halfHourLine);\n                        break;\n                    default:\n                        break;\n\n                }\n            } else {\n                line.setVisible(false);\n            }\n        }\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        super.layoutChildren(contentX, contentY, contentWidth, contentHeight);\n\n        backgroundCanvas.relocate(contentX, contentY);\n        backgroundCanvas.setWidth(contentWidth);\n        backgroundCanvas.setHeight(contentHeight);\n\n        backgroundCanvas.draw();\n\n        if (getSkinnable().isScrollingEnabled()) {\n            layoutChildrenInfiniteScrolling(contentX, contentY, contentWidth, contentHeight);\n        } else {\n            layoutChildrenStatic(contentX, contentY, contentWidth, contentHeight);\n        }\n    }\n\n    protected void layoutChildrenInfiniteScrolling(double contentX, double contentY, double contentWidth, double contentHeight) {\n        final T view = getSkinnable();\n        if (view.getGridType().equals(GridType.STANDARD)) {\n            final ZonedDateTime scrollTime = view.getScrollTime();\n            Instant time = scrollTime.toInstant().truncatedTo(ChronoUnit.HOURS);\n\n            double y = view.getLocation(time);\n\n            int lineIndex = 0;\n\n            do {\n\n                LocalTime localTime = LocalTime.ofInstant(time, view.getZoneId());\n\n                if (lineIndex >= lines.size()) {\n                    createLine();\n                    Line line = lines.get(lineIndex);\n                    line.toBack();\n                }\n\n                Line line = lines.get(lineIndex);\n                line.toBack();\n                line.setVisible(true);\n\n                line.getStyleClass().removeAll(HALF_HOUR_LINE_STYLE_CLASS, FULL_HOUR_LINE_STYLE_CLASS, MIDNIGHT_LINE_STYLE_CLASS, NOON_LINE_STYLE_CLASS);\n\n                if (localTime.getMinute() == 30) {\n                    line.getStyleClass().add(HALF_HOUR_LINE_STYLE_CLASS);\n                } else {\n                    line.getStyleClass().add(FULL_HOUR_LINE_STYLE_CLASS);\n                }\n\n                if (localTime.equals(LocalTime.MIDNIGHT)) {\n                    line.getStyleClass().add(MIDNIGHT_LINE_STYLE_CLASS);\n                    line.setStartX(snapPositionX(contentX));\n                    line.setEndX(snapPositionX(contentX + contentWidth));\n                } else if (localTime.equals(LocalTime.NOON)) {\n                    line.setStartX(snapPositionX(contentX));\n                    line.setEndX(snapPositionX(contentX + contentWidth));\n                    if (view.isShowNoonMarker()) {\n                        line.getStyleClass().add(NOON_LINE_STYLE_CLASS);\n                    }\n                } else {\n                    line.setStartX(snapPositionX(contentX + 4));\n                    line.setEndX(snapPositionX(contentX + contentWidth - 4));\n                }\n\n                line.setStartY(snapPositionY(y));\n                line.setEndY(snapPositionY(y));\n\n                lineIndex++;\n\n                time = time.plus(30, ChronoUnit.MINUTES);\n                y = view.getLocation(time);\n\n            } while (y < contentY + contentHeight);\n\n            for (int i = lineIndex; i < lines.size(); i++) {\n                Line line = lines.get(i);\n                line.setVisible(false);\n            }\n        }\n\n        layoutEntries(contentX, contentY, contentWidth, contentHeight);\n        layoutCurrentTime(contentX, contentY, contentWidth);\n    }\n\n    protected void layoutChildrenStatic(double contentX, double contentY, double contentWidth, double contentHeight) {\n        int lineCount = lines.size();\n\n        T dayView = getSkinnable();\n\n        boolean showEarlyHoursRegion = dayView.getStartTime().isAfter(LocalTime.MIN) && !dayView.getEarlyLateHoursStrategy().equals(EarlyLateHoursStrategy.HIDE);\n        boolean showLateHoursRegion = dayView.getEndTime().isBefore(LocalTime.MAX) && !dayView.getEarlyLateHoursStrategy().equals(EarlyLateHoursStrategy.HIDE);\n\n        earlyHoursRegion.setVisible(showEarlyHoursRegion);\n        lateHoursRegion.setVisible(showLateHoursRegion);\n\n        ZonedDateTime startTime = dayView.getZonedDateTimeStart();\n        ZonedDateTime endTime = dayView.getZonedDateTimeEnd();\n\n        double earlyHoursY = dayView.getLocation(startTime);\n        double lateHoursY = dayView.getLocation(endTime);\n\n        earlyHoursRegion.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY), snapSizeX(contentWidth), snapSizeY(earlyHoursY));\n        lateHoursRegion.resizeRelocate(snapPositionX(contentX), snapPositionY(lateHoursY), snapSizeX(contentWidth), snapSizeY(contentHeight - lateHoursY));\n\n        for (int i = 0; i < lineCount; i++) {\n\n            Line line = lines.get(i);\n\n            int hour = (i + 1) / 2;\n            int minute = 0;\n\n            boolean halfHourLine = (i % 2 == 0);\n            if (halfHourLine) {\n                minute = 30;\n            }\n\n            LocalTime time = LocalTime.of(hour, minute);\n            ZonedDateTime zonedDateTime = dayView.getZonedDateTime(time);\n\n            double yy = snapPositionY(contentY + dayView.getLocation(zonedDateTime));\n\n            line.setStartX(snapPositionX(contentX + 4));\n            line.setStartY(yy);\n            line.setEndX(snapPositionX(contentX + contentWidth - 4));\n            line.setEndY(yy);\n        }\n\n        // the dragged entry view\n        if (draggedEntryView != null) {\n            Entry<?> draggedEntry = draggedEntryView.getEntry();\n            draggedEntryView.visibleProperty().unbind();\n            draggedEntryView.setVisible(isRelevant(draggedEntry));\n        }\n\n        layoutEntries(contentX, contentY, contentWidth, contentHeight);\n        layoutCurrentTime(contentX, contentY, contentWidth);\n    }\n\n    protected void layoutEntries(double contentX, double contentY, double contentWidth, double contentHeight) {\n        T dayView = getSkinnable();\n\n        switch (dayView.getLayout()) {\n            case STANDARD:\n                layoutStandard(dayView, contentX, contentY, contentWidth, contentHeight);\n                break;\n            case SWIMLANE:\n                layoutSwimlane(dayView, contentX, contentY, contentWidth, contentHeight);\n                break;\n            default:\n                throw new IllegalArgumentException(\"unknown layout: \" + dayView.getLayout());\n        }\n    }\n\n    protected void layoutCurrentTime(double contentX, double contentY, double contentWidth) {\n        T dayView = getSkinnable();\n\n        double y;\n\n        ZonedDateTime time = dayView.getZonedDateTime();\n\n        if (dayView.isScrollingEnabled()) {\n            y = snapPositionY(dayView.getLocation(time));\n        } else {\n            y = snapPositionY(contentY + dayView.getLocation(time));\n        }\n\n        currentTimeLine.setStartX(snapPositionX(contentX));\n        currentTimeLine.setStartY(snapPositionY(y));\n        currentTimeLine.setEndX(snapPositionX(contentX + contentWidth));\n        currentTimeLine.setEndY(snapPositionY(y));\n        currentTimeLine.toFront();\n\n        currentTimeCircle.setCenterX(snapPositionX(contentX + currentTimeCircle.getRadius() + 4));\n        currentTimeCircle.setCenterY(y);\n        currentTimeCircle.toFront();\n    }\n\n    private void layoutStandard(DayView dayView, double contentX, double contentY, double contentWidth, double contentHeight) {\n\n        Predicate<DayEntryView> isRelatedToVisibleLayer = view -> dayView.visibleLayersProperty().contains(view.getLayer());\n\n        Map<Layer, List<DayEntryView>> layerGroupedEntryViews = groupEntryViewsBy(EntryViewBase::getLayer, isRelatedToVisibleLayer);\n\n        layoutOnLayers(layerGroupedEntryViews, dayView, contentX, contentY, contentWidth, contentHeight);\n    }\n\n    private void layoutSwimlane(DayView dayView, double contentX, double contentY, double contentWidth, double contentHeight) {\n        List<Calendar> visibleCalendars = dayView.getCalendars().filtered(c -> getSkinnable().isCalendarVisible(c));\n\n        double x = contentX;\n        double w = contentWidth / (visibleCalendars.size());\n\n        Predicate<DayEntryView> isRelatedToVisibleLayer = view -> dayView.visibleLayersProperty().contains(view.getLayer());\n        Predicate<DayEntryView> isRelatedToVisibleCalendar = view -> visibleCalendars.contains(getEntryViewCalendar(view));\n\n        Predicate<DayEntryView> entryViewFilter = isRelatedToVisibleLayer.and(isRelatedToVisibleCalendar);\n        Map<Calendar, List<DayEntryView>> calendarGroupedEntryViews = groupEntryViewsBy(this::getEntryViewCalendar, entryViewFilter);\n\n        for (Calendar calendar : visibleCalendars) {\n            Map<Layer, List<DayEntryView>> layerGroupedEntryViews = calendarGroupedEntryViews.getOrDefault(calendar, Collections.emptyList()).stream()\n                    .collect(Collectors.groupingBy(EntryViewBase::getLayer));\n\n            layoutOnLayers(layerGroupedEntryViews, dayView, x, contentY, w, contentHeight);\n            x += w;\n        }\n    }\n\n    private <G> Map<G, List<DayEntryView>> groupEntryViewsBy(Function<DayEntryView, G> groupByFunction, Predicate<DayEntryView> viewEntryFilter) {\n        return entryViewGroup.getChildren().stream()\n                .map(DayEntryView.class::cast)\n                .filter(viewEntryFilter)\n                .collect(Collectors.groupingBy(groupByFunction));\n    }\n\n    private void layoutOnLayers(Map<Layer, List<DayEntryView>> layerGroupedViewEntries, DayView dayView, double contentX, double contentY, double contentWidth, double contentHeight) {\n        List<DayEntryView> baseEntryViews = layerGroupedViewEntries.getOrDefault(DateControl.Layer.BASE, Collections.emptyList());\n        layoutBaseEntryViews(baseEntryViews, dayView, contentX, contentY, contentWidth, contentHeight);\n\n        List<DayEntryView> topEntryViews = layerGroupedViewEntries.getOrDefault(DateControl.Layer.TOP, Collections.emptyList());\n        layoutTopEntryViews(topEntryViews, dayView, contentX, contentY, contentWidth, contentHeight);\n    }\n\n    private Calendar getEntryViewCalendar(DayEntryView view) {\n        Calendar cal;\n        Entry<?> entry = view.getEntry();\n        if (entry instanceof DraggedEntry) {\n            DraggedEntry draggedEntry = (DraggedEntry) view.getEntry();\n            cal = draggedEntry.getOriginalCalendar();\n        } else {\n            cal = entry.getCalendar();\n        }\n        return cal;\n    }\n\n    protected void layoutBaseEntryViews(List<DayEntryView> entryViews, DayView dayView, double contentX, double contentY, double contentWidth, double contentHeight) {\n        List<Placement> placements;\n\n        if (dayView.getOverlapResolutionStrategy().equals(OverlapResolutionStrategy.VISUAL_BOUNDS)) {\n            placements = VisualBoundsResolver.resolve(entryViews, dayView, contentWidth);\n        } else {\n            placements = TimeBoundsResolver.resolve(entryViews);\n        }\n\n        if (placements != null) {\n            contentWidth = contentWidth * dayView.getEntryWidthPercentage() / 100d;\n\n            Instant dayViewStart = dayView.getZonedDateTime().with(LocalTime.MIN).toInstant();\n            Instant dayViewEnd = dayView.getZonedDateTimeEnd().with(LocalTime.MAX).toInstant();\n\n            for (Placement placement : placements) {\n                EntryViewBase<?> entryView = placement.getEntryView();\n\n                Entry<?> entry = entryView.getEntry();\n\n                double y1 = dayView.getLocation(entry.getStartAsZonedDateTime());\n                double y2 = dayView.getLocation(entry.getEndAsZonedDateTime());\n\n                if (entryView.getHeightLayoutStrategy().equals(HeightLayoutStrategy.COMPUTE_PREF_SIZE)) {\n                    y2 = y1 + entryView.prefHeight(contentWidth);\n                }\n\n                LocalDate viewDate = dayView.getDate();\n                LocalTime viewStartTime = entry.getStartTime();\n                LocalTime viewEndTime = entry.getEndTime();\n\n                if (!dayView.isScrollingEnabled()) {\n                    boolean startsBefore = false;\n                    boolean endsAfter = false;\n\n                    if (entry.getStartAsZonedDateTime().toInstant().isBefore(dayViewStart)) {\n                        y1 = contentY;\n                        viewStartTime = dayView.getStartTime();\n                        startsBefore = true;\n                    }\n\n                    if (entry.getEndAsZonedDateTime().toInstant().isAfter(dayViewEnd)) {\n                        y2 = contentHeight;\n                        viewEndTime = dayView.getEndTime();\n                        endsAfter = true;\n                    }\n\n                    Position position = Position.ONLY;\n\n                    if (startsBefore && endsAfter) {\n                        position = Position.MIDDLE;\n                    } else if (startsBefore) {\n                        position = Position.LAST;\n                    } else if (endsAfter) {\n                        position = Position.FIRST;\n                    }\n\n                    entryView.getProperties().put(\"position\", position);\n                }\n\n                entryView.getProperties().put(\"startDate\", viewDate);\n                entryView.getProperties().put(\"endDate\", viewDate);\n                entryView.getProperties().put(\"startTime\", viewStartTime);\n                entryView.getProperties().put(\"endTime\", viewEndTime);\n\n                double minHeight = entryView.minHeight(contentWidth);\n\n                double columnWidth = contentWidth / placement.getColumnCount();\n                double x = contentX;\n\n                if (!dayView.getOverlapResolutionStrategy().equals(OverlapResolutionStrategy.OFF)) {\n                    x += placement.getColumnIndex() * columnWidth;\n                }\n\n                double entryWidth = computeEntryWidth(entryView, columnWidth);\n                double entryLeftOffset = computeEntryLeftOffset(entryView, entryWidth, columnWidth);\n\n                /*\n                 * -2 on height to always have a gap between entries\n                 */\n                entryView.resizeRelocate(snapPositionX(x + entryLeftOffset), snapPositionY(y1), snapSizeX(entryWidth), snapSizeY(Math.max(minHeight, y2 - y1 - 2)));\n            }\n        }\n    }\n\n    protected void layoutTopEntryViews(List<DayEntryView> entryViews, DayView dayView, double contentX, double contentY, double contentWidth, double contentHeight) {\n\n        entryViews.sort(Comparator.comparing(EntryViewBase::getStartDate));\n\n        for (DayEntryView entryView : entryViews) {\n            Entry<?> entry = entryView.getEntry();\n\n            double y1 = dayView.getLocation(entry.getStartAsZonedDateTime());\n            double y2 = dayView.getLocation(entry.getEndAsZonedDateTime());\n\n            double entryWidth = computeEntryWidth(entryView, contentWidth);\n            double entryLeftOffset = computeEntryLeftOffset(entryView, entryWidth, contentWidth);\n\n            double minHeight = entryView.minHeight(entryWidth);\n\n            entryView.resizeRelocate(snapPositionX(contentX + entryLeftOffset), snapPositionY(y1), snapSizeX(entryWidth), snapSizeY(Math.max(minHeight, y2 - y1 - 2)));\n            entryView.toFront();\n        }\n    }\n\n    /**\n     * Compute entry width during layout phase.\n     *\n     * @param entryView      view entry\n     * @param availableWidth maximum available horizontal space for entry view\n     */\n    private double computeEntryWidth(EntryViewBase<?> entryView, double availableWidth) {\n        if (entryView.getAlignmentStrategy().equals(AlignmentStrategy.FILL)) {\n            return availableWidth;\n        }\n        double preferredWidth = entryView.prefWidth(-1);\n        if (preferredWidth != 0.0) {\n            return preferredWidth;\n        }\n        return availableWidth * (entryView.getWidthPercentage() * 0.01);\n    }\n\n    private double computeEntryLeftOffset(EntryViewBase<?> entryView, double entryWidth, double availableWidth) {\n        switch (entryView.getAlignmentStrategy()) {\n            default:\n            case FILL:\n            case ALIGN_LEFT:\n                return 0.0;\n            case ALIGN_CENTER:\n                return (availableWidth - entryWidth) * 0.5;\n            case ALIGN_RIGHT:\n                return (availableWidth - entryWidth);\n        }\n    }\n\n    @Override\n    protected void zoneIdChanged() {\n        if (!getSkinnable().isSuspendUpdates()) {\n            loadData(\"zone ID changed\");\n        }\n    }\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        LoggingDomain.VIEW.fine(\"handle calendar changed, date = \" + getSkinnable().getDate());\n        if (!getSkinnable().isSuspendUpdates()) {\n            loadData(\"changes in calendar \" + calendar.getName());\n        }\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry calendar changed, date = \" + getSkinnable().getDate());\n\n        Entry<?> entry = evt.getEntry();\n        if (evt.getCalendar() == null) {\n            if (findEntryView(entry).isPresent()) {\n                removeEntryView(entry, \"entry was deleted\");\n            }\n        } else {\n            if (!entry.isFullDay() && evt.getOldCalendar() == null && isRelevant(entry)) {\n                addEntryView(entry, \"entry calendar changed\");\n            }\n        }\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry full day flag changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n        if (isRelevant(entry)) {\n            Optional<EntryViewBase> entryView = findEntryView(entry);\n            if (entry.isFullDay() && entryView.isPresent()) {\n                removeEntryView(entry, \"full day flag changed to true\");\n            } else {\n                addEntryView(entry, \"full day flag changed to false, no entry view can be present\");\n            }\n        }\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry recurrence rule changed, date = \" + getSkinnable().getDate());\n        Entry<?> entry = evt.getEntry();\n\n        /*\n         * We do not care about full day entries in this view.\n         */\n        if (!entry.isFullDay() && !entry.getStartDate().equals(getSkinnable().getDate())) {\n            Optional<Entry<?>> recurrenceEntry = findRecurrenceEntry(entry);\n\n            if (recurrenceEntry.isPresent()) {\n                Optional<EntryViewBase> entryView = findEntryView(entry);\n                if (entryView.isPresent()) {\n                    entryView.get().setEntry(recurrenceEntry.get());\n                } else {\n                    addEntryView(entry, \"recurrence rule changed, no view was present\");\n                }\n            } else {\n                removeEntryView(entry, \"recurrence rule changed, no recurrence found\");\n            }\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        LoggingDomain.VIEW.fine(\"handle entry interval changed, date = \" + getSkinnable().getDate());\n\n        Entry<?> entry = evt.getEntry();\n\n        /*\n         * We do not care about full day entries in this view.\n         */\n        if (!entry.isFullDay()) {\n            Optional<EntryViewBase> entryView = findEntryView(entry);\n            if (isRelevant(entry)) {\n                if (entryView.isPresent()) {\n                    if (entry.isRecurring() && !entry.getStartDate().equals(getSkinnable().getDate())) {\n                        Optional<Entry<?>> recurrenceEntry = findRecurrenceEntry(entry);\n                        if (recurrenceEntry.isPresent()) {\n                            entryView.get().setEntry(recurrenceEntry.get());\n                        }\n                    } else {\n                        entryView.get().setEntry(entry);\n                    }\n                } else {\n                    addEntryView(entry, \"interval changed, no view was present\");\n                }\n            } else {\n                removeEntryView(entry, \"interval changed, entry is not relevant\");\n            }\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    private Optional<EntryViewBase> findEntryView(Entry<?> entry) {\n        List<EntryViewBase> collect = entryViewGroup.getChildren().stream().map(node -> (EntryViewBase) node).filter(e -> e.getEntry().getId().equals(entry.getId())).collect(Collectors.toList());\n        if (collect.isEmpty()) {\n            return Optional.empty();\n        }\n\n        return Optional.of(collect.get(0));\n    }\n\n    private boolean removeEntryView(Entry<?> entry, String reason) {\n        if (reason != null) {\n            LoggingDomain.VIEW.fine(\"removing entry, reason = \" + reason + \", date = \" + getSkinnable().getDate());\n        }\n        boolean removed = Util.removeChildren(entryViewGroup, node -> {\n            DayEntryView view = (DayEntryView) node;\n\n            Entry<?> removedEntry = entry;\n            if (removedEntry.getRecurrenceSourceEntry() != null) {\n                removedEntry = removedEntry.getRecurrenceSourceEntry();\n            }\n\n            Entry<?> viewEntry = view.getEntry();\n            if (viewEntry.getRecurrenceSourceEntry() != null) {\n                viewEntry = viewEntry.getRecurrenceSourceEntry();\n            }\n\n            return viewEntry.getId().equals(removedEntry.getId());\n        });\n\n        if (removed && !(entry instanceof DraggedEntry) && LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"successfully removed the entry view of entry \" + entry);\n        }\n\n        return removed;\n    }\n\n    private void addEntryView(Entry<?> entry, String reason) {\n        LoggingDomain.VIEW.fine(\"adding entry, reason = \" + reason + \", date = \" + getSkinnable().getDate());\n        if (entry.isRecurring()) {\n            Optional<Entry<?>> recurrenceEntry = findRecurrenceEntry(entry);\n            if (recurrenceEntry.isPresent()) {\n                doAddEntryView(recurrenceEntry.get());\n            }\n        } else {\n            doAddEntryView(entry);\n        }\n\n        getSkinnable().requestLayout();\n    }\n\n    private Optional<Entry<?>> findRecurrenceEntry(Entry<?> entry) {\n        Calendar calendar = entry.getCalendar();\n        LocalDate date = getSkinnable().getDate();\n        final Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(date, date, getZoneId());\n        List<Entry<?>> entriesOnDate = entries.get(date);\n        if (entriesOnDate != null && !entriesOnDate.isEmpty()) {\n            return entriesOnDate.stream().filter(e -> e.getId().equals(entry.getId())).findFirst();\n        }\n\n        return Optional.empty();\n    }\n\n    private DayEntryView doAddEntryView(Entry<?> entry) {\n        Callback<Entry<?>, DayEntryView> factory = getSkinnable().getEntryViewFactory();\n\n        DayEntryView view = factory.call(entry);\n        view.getProperties().put(\"control\", getSkinnable());\n        view.setManaged(false);\n\n        int index = findIndex(entry);\n\n        entryViewGroup.getChildren().add(index, view);\n\n        if (!(entry instanceof DraggedEntry) && LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"added entry view \" + entry.getTitle() + \", day = \" + getSkinnable().getDate());\n        }\n\n        return view;\n    }\n\n    /*\n     * Utility method to find the right place for inserting a new day entry\n     * view. The right order is important for TAB traversal to work properly.\n     */\n    private int findIndex(Entry<?> entry) {\n        int childrenSize = entryViewGroup.getChildren().size();\n\n        for (int i = 0; i < childrenSize; i++) {\n            Node node = entryViewGroup.getChildren().get(i);\n            DayEntryView view = (DayEntryView) node;\n            Entry<?> viewEntry = view.getEntry();\n            if (viewEntry.getStartAsZonedDateTime().isAfter(entry.getStartAsZonedDateTime())) {\n                return i;\n            }\n        }\n\n        return childrenSize;\n    }\n\n    private void updateEntries(String reason) {\n        displayedDate = getSkinnable().getDate();\n\n        entryViewGroup.getChildren().clear();\n\n        Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n        dataLoader.loadEntries(dataMap);\n\n        LocalDate date = getLoadStartDate();\n\n        LocalTime earliest = null;\n        LocalTime latest = null;\n\n        List<Entry> processedEntries = new ArrayList<>();\n\n        do {\n            List<Entry<?>> entryList = dataMap.get(date);\n\n            if (entryList != null) {\n                entryList.removeIf(Entry::isFullDay);\n\n                for (Entry<?> entry : entryList) {\n\n                    if (processedEntries.contains(entry)) {\n                        continue;\n                    }\n\n                    processedEntries.add(entry);\n\n                    doAddEntryView(entry);\n\n                    if (earliest == null || entry.getStartTime().isBefore(earliest)) {\n                        earliest = entry.getStartTime();\n                    }\n\n                    if (entry.getStartDate().isBefore(getSkinnable().getDate())) {\n                        earliest = LocalTime.MIN;\n                    }\n\n                    if (latest == null || entry.getEndTime().isAfter(latest)) {\n                        latest = entry.getEndTime();\n                    }\n\n                    if (entry.getEndDate().isAfter(getSkinnable().getDate())) {\n                        latest = LocalTime.MAX;\n                    }\n                }\n            }\n\n            date = date.plusDays(1);\n        } while (!date.isAfter(getLoadEndDate()));\n\n        getSkinnable().getProperties().put(\"earliest.time.used\", earliest);\n        getSkinnable().getProperties().put(\"latest.time.used\", latest);\n\n        getSkinnable().requestLayout();\n\n        LoggingDomain.VIEW.fine(\"updating entries in day view \" + getSkinnable().getDate() + \": reason = \" + reason + \", entry count: \" + entryViewGroup.getChildren().size());\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"Day View\";\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        if (getSkinnable().isScrollingEnabled()) {\n            return getSkinnable().getScrollTime().toLocalDate().minusDays(1);\n        }\n\n        return getSkinnable().getDate();\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        if (getSkinnable().isScrollingEnabled()) {\n            return getSkinnable().getZonedDateTimeAt(0, getSkinnable().getHeight(), getSkinnable().getZoneId()).toLocalDate();\n        }\n\n        return getSkinnable().getDate();\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        return getSkinnable().getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n\n    private class BackgroundCanvas extends Canvas {\n\n        BackgroundCanvas() {\n            setMouseTransparent(true);\n\n            InvalidationListener redrawListener = it -> draw();\n            heightProperty().addListener(redrawListener);\n            widthProperty().addListener(redrawListener);\n        }\n\n        public boolean isResizable() {\n            return true;\n        }\n\n        public void draw() {\n            GraphicsContext gc = backgroundCanvas.getGraphicsContext2D();\n            gc.clearRect(0, 0, getWidth(), getHeight());\n\n            T view = getSkinnable();\n            Calendar availabilityCalendar = view.getAvailabilityCalendar();\n\n            if (availabilityCalendar != null) {\n                gc.setFill(view.getAvailabilityFill());\n                LocalDate date = view.getDate();\n                Map<LocalDate, List<Entry<?>>> entries = availabilityCalendar.findEntries(date, date, view.getZoneId());\n                List<Entry<?>> entriesOnDate = entries.get(date);\n                if (entriesOnDate != null) {\n                    entriesOnDate.forEach(entry -> {\n                        ZonedDateTime startAsZonedDateTime = entry.getStartAsZonedDateTime();\n                        ZonedDateTime endAsZonedDateTime = entry.getEndAsZonedDateTime();\n                        double y1 = ViewHelper.getTimeLocation(view, startAsZonedDateTime);\n                        double y2 = ViewHelper.getTimeLocation(view, endAsZonedDateTime);\n                        gc.fillRect(0, y1, getWidth(), y2 - y1);\n                    });\n                }\n            }\n\n            if (view.isEditAvailability()) {\n                Instant start = view.getLassoStart();\n                Instant end = view.getLassoEnd();\n\n                if (start != null && end != null) {\n                    double y1 = ViewHelper.getTimeLocation(view, start);\n                    double y2 = ViewHelper.getTimeLocation(view, end);\n\n                    double minY = Math.min(y1, y2);\n                    double maxY = Math.max(y1, y2);\n\n                    gc.setFill(view.getLassoColor());\n                    gc.fillRect(0, minY, getWidth(), maxY - minY);\n                }\n            }\n\n            VirtualGrid virtualGrid = view.getGridLines();\n\n            if (view.getGridType().equals(GridType.CUSTOM)) {\n                gc.setStroke(view.getGridLineColor());\n\n                if (view.isScrollingEnabled()) {\n                    ZonedDateTime time = view.getScrollTime();\n                    time = virtualGrid.adjustTime(time, false, view.getFirstDayOfWeek());\n\n                    double y = view.getLocation(time);\n\n                    do {\n                        if (time.toLocalTime().getMinute() == 0) {\n                            gc.setLineDashes(null);\n                        } else {\n                            gc.setLineDashes(2, 2);\n                        }\n                        gc.strokeLine(0, y, getWidth(), y);\n                        time = time.plus(virtualGrid.getAmount(), virtualGrid.getUnit());\n                        y = view.getLocation(time);\n                    } while (y < getHeight());\n\n                    gc.setLineDashes(null);\n                } else {\n                    ZonedDateTime startTime = view.getZonedDateTimeMin();\n                    ZonedDateTime endTime = view.getZonedDateTimeMax();\n\n                    if (view.getEarlyLateHoursStrategy().equals(EarlyLateHoursStrategy.HIDE)) {\n                        startTime = view.getZonedDateTimeStart();\n                        endTime = view.getZonedDateTimeEnd();\n                    }\n\n                    do {\n                        double y = ViewHelper.getTimeLocation(view, startTime);\n                        if (startTime.toLocalTime().getMinute() == 0) {\n                            gc.setLineDashes(null);\n                        } else {\n                            gc.setLineDashes(2, 2);\n                        }\n                        gc.strokeLine(0, y, getWidth(), y);\n                        startTime = startTime.plus(virtualGrid.getAmount(), virtualGrid.getUnit());\n                    } while (startTime.isBefore(endTime));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DetailedDayViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.CalendarHeaderView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DetailedDayView;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.TimeScaleView;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.Orientation;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.OverrunStyle;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.control.Separator;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\n\nimport static com.calendarfx.util.ViewHelper.scrollToRequestedTime;\n\npublic class DetailedDayViewSkin extends DateControlSkin<DetailedDayView> {\n\n    private final GridPane gridPane;\n    private final Label allDayLabel;\n    private final DayViewScrollPane timeScaleScrollPane;\n    private final DayViewScrollPane dayViewScrollPane;\n    private final Separator separator;\n    private final AllDayView allDayView;\n    private final ScrollBar scrollBar;\n    private final CalendarHeaderView calendarHeaderView;\n    private final Region allDayFiller;\n    private final AgendaView agendaView;\n    private final ColumnConstraints col0;\n    private final ColumnConstraints col1;\n    private final ColumnConstraints col2;\n    private final ColumnConstraints col3;\n    private final ColumnConstraints col4;\n\n    public DetailedDayViewSkin(DetailedDayView view) {\n        super(view);\n\n        scrollBar = new ScrollBar();\n\n        // the day view (scroll pane)\n        DayView dayView = view.getDayView();\n        dayViewScrollPane = new DayViewScrollPane(dayView, scrollBar);\n        dayViewScrollPane.getStyleClass().addAll(\"calendar-scroll-pane\", \"day-view-scroll-pane\");\n\n        // the timescale\n        TimeScaleView timeScale = view.getTimeScaleView();\n        Bindings.bindBidirectional(timeScale.translateYProperty(), dayView.translateYProperty());\n\n        // the all-day view\n        allDayView = view.getAllDayView();\n        allDayView.showTodayProperty().unbindBidirectional(view.showTodayProperty());\n        allDayView.setShowToday(false);\n        allDayView.setAdjustToFirstDayOfWeek(false);\n\n        // all day label\n        allDayLabel = new Label(Messages.getString(\"DetailedDayViewSkin.ALL_DAY\"));\n        allDayLabel.setTextOverrun(OverrunStyle.CLIP);\n        allDayLabel.getStyleClass().add(\"all-day-label\");\n        allDayLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        // all day filler\n        allDayFiller = new Region();\n        allDayFiller.getStyleClass().add(\"header-all-day-filler\");\n\n        // time scale scroll pane\n        timeScaleScrollPane = new DayViewScrollPane(timeScale, scrollBar);\n        timeScaleScrollPane.getStyleClass().addAll(\"calendar-scroll-pane\", \"day-view-timescale-scroll-pane\");\n        timeScaleScrollPane.setMinWidth(Region.USE_PREF_SIZE);\n\n        // separator\n        separator = new Separator(Orientation.VERTICAL);\n\n        final InvalidationListener visibilityListener = it -> updateVisibilities();\n        view.showAllDayViewProperty().addListener(visibilityListener);\n        view.showTimeScaleViewProperty().addListener(visibilityListener);\n        view.layoutProperty().addListener(visibilityListener);\n        view.showAgendaViewProperty().addListener(visibilityListener);\n        view.showScrollBarProperty().addListener(visibilityListener);\n\n        calendarHeaderView = view.getCalendarHeaderView();\n        calendarHeaderView.visibleProperty().bind(view.layoutProperty().isEqualTo(DateControl.Layout.SWIMLANE));\n\n        agendaView = view.getAgendaView();\n\n        RowConstraints row0 = new RowConstraints();\n        row0.setFillHeight(true);\n        row0.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row0.setVgrow(Priority.NEVER);\n\n        RowConstraints row1 = new RowConstraints();\n        row1.setFillHeight(true);\n        row1.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row1.setVgrow(Priority.NEVER);\n\n        RowConstraints row2 = new RowConstraints();\n        row2.setFillHeight(true);\n        row2.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row2.setVgrow(Priority.ALWAYS);\n\n        col0 = new ColumnConstraints();\n        col0.setFillWidth(true);\n        col0.setHgrow(Priority.NEVER);\n        col0.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        col1 = new ColumnConstraints();\n        col1.setFillWidth(true);\n        col1.setHgrow(Priority.ALWAYS);\n        col1.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        col2 = new ColumnConstraints();\n        col2.setFillWidth(true);\n        col2.setHgrow(Priority.NEVER);\n        col2.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        col3 = new ColumnConstraints();\n        col3.setFillWidth(true);\n        col3.setHgrow(Priority.NEVER);\n        col3.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        col4 = new ColumnConstraints();\n        col4.setFillWidth(true);\n        col4.setHgrow(Priority.ALWAYS);\n        col4.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        GridPane.setRowSpan(agendaView, 3);\n        GridPane.setRowSpan(separator, 3);\n\n        gridPane = new GridPane();\n        gridPane.getRowConstraints().setAll(row0, row1, row2);\n        gridPane.getStyleClass().add(\"container\");\n\n        getChildren().add(gridPane);\n\n        agendaView.setPrefWidth(0);\n        dayView.setPrefWidth(0);\n\n        /*\n         * Run later when the control has become visible.\n         */\n        Platform.runLater(() -> scrollToRequestedTime(view, dayViewScrollPane));\n\n        view.requestedTimeProperty().addListener(it -> scrollToRequestedTime(view, dayViewScrollPane));\n\n        updateVisibilities();\n    }\n\n    private void updateVisibilities() {\n        gridPane.getChildren().clear();\n\n        final DetailedDayView view = getSkinnable();\n\n        if (view.isShowTimeScaleView()) {\n            gridPane.add(timeScaleScrollPane, 0, 2);\n            if (view.isShowAllDayView()) {\n                gridPane.add(allDayLabel, 0, 0);\n            }\n        }\n\n        if (view.isShowAllDayView()) {\n            gridPane.add(allDayView, 1, 0);\n            gridPane.add(allDayFiller, 2, 0);\n        }\n\n        if (view.getLayout().equals(DateControl.Layout.SWIMLANE)) {\n            gridPane.add(calendarHeaderView, 1, 1);\n        }\n\n        gridPane.add(dayViewScrollPane, 1, 2);\n\n        if (view.isShowScrollBar()) {\n            gridPane.add(scrollBar, 2, 2);\n        }\n\n        if (view.isShowAgendaView()) {\n            gridPane.getColumnConstraints().setAll(col0, col1, col2, col3, col4);\n            gridPane.add(separator, 3, 0);\n            gridPane.add(agendaView, 4, 0);\n        } else {\n            gridPane.getColumnConstraints().setAll(col0, col1, col2);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DetailedWeekViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.CalendarHeaderView;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.WeekDayHeaderView;\nimport com.calendarfx.view.WeekTimeScaleView;\nimport com.calendarfx.view.WeekView;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.OverrunStyle;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\n\nimport static com.calendarfx.util.ViewHelper.scrollToRequestedTime;\n\npublic class DetailedWeekViewSkin extends DateControlSkin<DetailedWeekView> {\n\n    private final Label allDayLabel;\n    private final DayViewScrollPane weekViewScrollPane;\n    private final DayViewScrollPane timeScaleScrollPane;\n    private final GridPane weekViewContainer;\n    private final AllDayView allDayView;\n    private final CalendarHeaderView calendarHeaderView;\n    private final WeekDayHeaderView weekdayHeaderView;\n    private final ScrollBar scrollBar;\n    private final Region weekdayFillerLeft;\n    private final Region weekdayFillerRight;\n    private final Region allDayFiller;\n\n    public DetailedWeekViewSkin(DetailedWeekView view) {\n        super(view);\n\n        WeekView weekView = view.getWeekView();\n        allDayView = view.getAllDayView();\n        calendarHeaderView = view.getCalendarHeaderView();\n        scrollBar = new ScrollBar();\n\n        weekViewScrollPane = new DayViewScrollPane(weekView, scrollBar);\n        weekViewScrollPane.getStyleClass().add(\"week-view-scroll-pane\");\n\n        allDayLabel = new Label(Messages.getString(\"DetailedWeekViewSkin.ALL_DAY\"));\n        allDayLabel.setTextOverrun(OverrunStyle.CLIP);\n        allDayLabel.getStyleClass().add(\"all-day-label\");\n        allDayLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        weekdayFillerLeft = new Region();\n        weekdayFillerLeft.getStyleClass().addAll(\"filler-left\");\n\n        weekdayFillerRight = new Region();\n        weekdayFillerRight.getStyleClass().addAll(\"filler-right\");\n\n        allDayFiller = new Region();\n        allDayFiller.getStyleClass().add(\"all-day-filler\");\n\n        WeekTimeScaleView timeScale = view.getTimeScaleView();\n        timeScale.getProperties().put(\"week.view\", view);\n\n        timeScaleScrollPane = new DayViewScrollPane(timeScale, scrollBar);\n        timeScaleScrollPane.getStyleClass().addAll(\"timescale-scroll-pane\");\n        timeScaleScrollPane.setMinWidth(Region.USE_PREF_SIZE);\n\n        // synchronous scrolling\n        Bindings.bindBidirectional(timeScale.translateYProperty(), weekView.translateYProperty());\n\n        weekdayHeaderView = view.getWeekDayHeaderView();\n\n        RowConstraints row0 = new RowConstraints();\n        row0.setFillHeight(true);\n        row0.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row0.setVgrow(Priority.NEVER);\n\n        RowConstraints row1 = new RowConstraints();\n        row1.setFillHeight(true);\n        row1.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row1.setVgrow(Priority.NEVER);\n\n        RowConstraints row2 = new RowConstraints();\n        row2.setFillHeight(true);\n        row2.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row2.setVgrow(Priority.NEVER);\n\n        RowConstraints row3 = new RowConstraints();\n        row3.setFillHeight(true);\n        row3.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row3.setVgrow(Priority.ALWAYS);\n\n        ColumnConstraints col0 = new ColumnConstraints();\n        col0.setFillWidth(true);\n        col0.setHgrow(Priority.NEVER);\n        col0.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        col1.setFillWidth(true);\n        col1.setHgrow(Priority.ALWAYS);\n        col1.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        ColumnConstraints col2 = new ColumnConstraints();\n        col2.setFillWidth(true);\n        col2.setHgrow(Priority.NEVER);\n        col2.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n        weekViewContainer = new GridPane();\n        weekViewContainer.getStyleClass().add(\"container\");\n        weekViewContainer.setGridLinesVisible(true);\n        weekViewContainer.getRowConstraints().setAll(row0, row1, row2, row3);\n        weekViewContainer.getColumnConstraints().setAll(col0, col1, col2);\n\n        getChildren().add(weekViewContainer);\n\n        final InvalidationListener visibilityListener = it -> updateVisibilities();\n        view.showTimeScaleViewProperty().addListener(visibilityListener);\n        view.showWeekDayHeaderViewProperty().addListener(visibilityListener);\n        view.showAllDayViewProperty().addListener(visibilityListener);\n        view.layoutProperty().addListener(visibilityListener);\n        view.showScrollBarProperty().addListener(visibilityListener);\n\n        /*\n         * Run later when the control has become visible.\n         */\n        Platform.runLater(() -> scrollToRequestedTime(view, weekViewScrollPane));\n\n        view.requestedTimeProperty().addListener(it -> scrollToRequestedTime(view, weekViewScrollPane));\n\n        updateVisibilities();\n    }\n\n    private void updateVisibilities() {\n        weekViewContainer.getChildren().clear();\n\n        final DetailedWeekView view = getSkinnable();\n\n        if (view.isShowTimeScaleView()) {\n            weekViewContainer.add(timeScaleScrollPane, 0, 3);\n            if (view.isShowAllDayView()) {\n                weekViewContainer.add(allDayLabel, 0, 1);\n            }\n        }\n\n        if (view.isShowWeekDayHeaderView()) {\n            weekViewContainer.add(weekdayFillerLeft, 0, 0);\n            weekViewContainer.add(weekdayHeaderView, 1, 0);\n            weekViewContainer.add(weekdayFillerRight, 2, 0);\n        }\n\n        if (view.isShowAllDayView()) {\n            weekViewContainer.add(allDayView, 1, 1);\n            weekViewContainer.add(allDayFiller, 2, 1);\n        }\n\n        if (view.getLayout().equals(DateControl.Layout.SWIMLANE)) {\n            weekViewContainer.add(calendarHeaderView, 1, 2);\n        }\n\n        weekViewContainer.add(weekViewScrollPane, 1, 3);\n\n        if (view.isShowScrollBar()) {\n            weekViewContainer.add(scrollBar, 2, 3);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/DeveloperConsoleSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.model.LoadEvent;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DeveloperConsole;\nimport com.calendarfx.view.RequestEvent;\nimport com.calendarfx.view.TimeField;\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.FilteredList;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.event.WeakEventHandler;\nimport javafx.geometry.Side;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.ToolBar;\nimport javafx.scene.control.cell.PropertyValueFactory;\nimport javafx.scene.layout.BorderPane;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\n\nimport static java.util.Objects.requireNonNull;\n\n/**\n * A control used for showing the internals of CalendarFX at work. Helps\n * detecting problems.\n */\npublic class DeveloperConsoleSkin extends SkinBase<DeveloperConsole> {\n\n    private final TableView<LogEntry> tableView;\n    private final FilteredList<LogEntry> filteredList;\n    private final ToggleButton showLoadEvents;\n    private final ToggleButton showCalendarEvents;\n    private final ToggleButton showRequestEvents;\n    private final DatePicker datePicker;\n    private final DatePicker todayPicker;\n    private final TimeField timeField;\n\n    private final ObservableList<LogEntry> masterData = FXCollections.observableArrayList();\n    private final EventHandler<CalendarEvent> calendarListener = evt -> addEvent(evt, LogEntryType.CALENDAR_EVENT);\n    private final WeakEventHandler<CalendarEvent> weakCalendarListener = new WeakEventHandler<>(calendarListener);\n\n    public DeveloperConsoleSkin(DeveloperConsole view) {\n        super(view);\n\n        TabPane tabPane = view.getTabPane();\n        tabPane.setSide(Side.TOP);\n\n        getChildren().add(tabPane);\n\n        this.tableView = new TableView<>();\n\n        ToolBar toolbar = new ToolBar();\n\n        this.datePicker = new DatePicker();\n        this.todayPicker = new DatePicker();\n        this.timeField = new TimeField();\n\n        showCalendarEvents = new ToggleButton(\"Calendar Events\");\n        showCalendarEvents.setSelected(true);\n        showCalendarEvents.setOnAction(evt -> filter());\n        toolbar.getItems().add(showCalendarEvents);\n\n        showLoadEvents = new ToggleButton(\"Load Events\");\n        showLoadEvents.setSelected(false);\n        showLoadEvents.setOnAction(evt -> filter());\n        toolbar.getItems().add(showLoadEvents);\n\n        showRequestEvents = new ToggleButton(\"Request Events\");\n        showRequestEvents.setSelected(false);\n        showRequestEvents.setOnAction(evt -> filter());\n        toolbar.getItems().add(showRequestEvents);\n\n        toolbar.getItems().add(new Separator());\n\n        Button clearLog = new Button(\"Clear\");\n        clearLog.setOnAction(evt -> {\n            masterData.clear();\n            LogEntry.counter = 0;\n        });\n\n        toolbar.getItems().add(clearLog);\n\n        toolbar.getItems().add(new Separator());\n\n        toolbar.getItems().add(new Label(\"Date:\"));\n        toolbar.getItems().add(datePicker);\n\n        toolbar.getItems().add(new Label(\"Today:\"));\n        toolbar.getItems().add(todayPicker);\n\n        toolbar.getItems().add(new Label(\"Time:\"));\n        toolbar.getItems().add(timeField);\n\n        BorderPane eventsBorderPane = new BorderPane();\n        eventsBorderPane.setBottom(toolbar);\n        eventsBorderPane.setCenter(tableView);\n\n        Tab tab = new Tab(\"Events\", eventsBorderPane);\n        tabPane.getTabs().add(tab);\n\n        TableColumn<LogEntry, Integer> counterColumn = new TableColumn<>(\"#\");\n        counterColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"counter\"));\n        counterColumn.setPrefWidth(50);\n\n        TableColumn<LogEntry, LogEntryType> logEntryTypeColumn = new TableColumn<>(\n                \"Event\");\n        logEntryTypeColumn.setCellValueFactory(\n                new PropertyValueFactory<>(\"logEntryType\"));\n        logEntryTypeColumn.setPrefWidth(200);\n\n        TableColumn<LogEntry, String> eventTypeColumn = new TableColumn<>(\n                \"Event Type\");\n        eventTypeColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"eventType\"));\n        eventTypeColumn.setPrefWidth(200);\n\n        TableColumn<LogEntry, String> sourceColumn = new TableColumn<>(\n                \"Source\");\n        sourceColumn.setCellValueFactory(new PropertyValueFactory<>(\"source\"));\n        sourceColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, String> targetColumn = new TableColumn<>(\n                \"Target\");\n        targetColumn.setCellValueFactory(new PropertyValueFactory<>(\"target\"));\n        targetColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, LocalDateTime> newStartTimeColumn = new TableColumn<>(\n                \"Start\");\n        newStartTimeColumn.setCellValueFactory(\n                new PropertyValueFactory<>(\"newStartTime\"));\n        newStartTimeColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, LocalDateTime> newEndTimeColumn = new TableColumn<>(\n                \"End\");\n        newEndTimeColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"newEndTime\"));\n        newEndTimeColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, LocalDateTime> oldStartTimeColumn = new TableColumn<>(\n                \"Old Start\");\n        oldStartTimeColumn.setCellValueFactory(\n                new PropertyValueFactory<>(\"oldStartTime\"));\n        oldStartTimeColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, LocalDateTime> oldEndTimeColumn = new TableColumn<>(\n                \"Old End\");\n        oldEndTimeColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"oldEndTime\"));\n        oldEndTimeColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, String> timestampColumn = new TableColumn<>(\n                \"Timestamp\");\n        timestampColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"timestamp\"));\n        timestampColumn.setPrefWidth(120);\n\n        TableColumn<LogEntry, String> descriptionColumn = new TableColumn<>(\n                \"Description\");\n        descriptionColumn\n                .setCellValueFactory(new PropertyValueFactory<>(\"description\"));\n        descriptionColumn.setPrefWidth(700);\n\n        tableView.getColumns().setAll(counterColumn, logEntryTypeColumn,\n                eventTypeColumn, sourceColumn, targetColumn, newStartTimeColumn,\n                newEndTimeColumn, oldStartTimeColumn, oldEndTimeColumn,\n                timestampColumn, descriptionColumn);\n\n        filteredList = new FilteredList<>(masterData);\n\n        tableView.setItems(filteredList);\n\n        filter();\n\n        updateSkin();\n\n        view.dateControlProperty().addListener(it -> updateSkin());\n    }\n\n    private void updateSkin() {\n        DeveloperConsole view = getSkinnable();\n        if (view.getDateControl() != null) {\n            setDateControl(view.getDateControl());\n        }\n    }\n\n    /**\n     * Sets the control that will be \"monitored\" by the developer console.\n     *\n     * @param control\n     *            the monitored control\n     */\n    private void setDateControl(DateControl control) {\n        requireNonNull(control);\n\n        control.addEventFilter(RequestEvent.REQUEST, evt -> addEvent(evt, LogEntryType.REQUEST_EVENT));\n        control.addEventFilter(LoadEvent.LOAD, evt -> addEvent(evt, LogEntryType.LOAD_EVENT));\n\n        // listen to calendars\n\n        for (Calendar calendar : control.getCalendars()) {\n            calendar.addEventHandler(weakCalendarListener);\n        }\n\n        ListChangeListener<? super Calendar> l = change -> {\n            while (change.next()) {\n                if (change.wasAdded()) {\n                    for (Calendar c : change.getAddedSubList()) {\n                        c.addEventHandler(weakCalendarListener);\n                    }\n                } else if (change.wasRemoved()) {\n                    for (Calendar c : change.getRemoved()) {\n                        c.removeEventHandler(weakCalendarListener);\n                    }\n                }\n            }\n        };\n\n        control.getCalendars().addListener(l);\n\n        Bindings.bindBidirectional(datePicker.valueProperty(), control.dateProperty());\n        Bindings.bindBidirectional(todayPicker.valueProperty(), control.todayProperty());\n        Bindings.bindBidirectional(timeField.valueProperty(), control.timeProperty());\n        timeField.setDisable(false);\n    }\n\n    private void filter() {\n        filteredList.setPredicate(item -> {\n            switch (item.getLogEntryType()) {\n                case CALENDAR_EVENT:\n                    return showCalendarEvents.isSelected();\n                case LOAD_EVENT:\n                    return showLoadEvents.isSelected();\n                case REQUEST_EVENT:\n                    return showRequestEvents.isSelected();\n                case INFO:\n                default:\n                    return true;\n            }\n        });\n    }\n\n    private void addEvent(Event evt, LogEntryType type) {\n        LogEntry entry = new LogEntry(type, evt);\n        masterData.add(entry);\n        limitListSize();\n        tableView.scrollTo(entry);\n    }\n\n    private void limitListSize() {\n        if (masterData.size() > 100) {\n            masterData.remove(0, 10);\n        }\n    }\n\n    enum LogEntryType {\n        INFO, CALENDAR_EVENT, REQUEST_EVENT, LOAD_EVENT\n    }\n\n    public static class LogEntry {\n\n        private static int counter = 0;\n\n        private final LocalDateTime timestamp = LocalDateTime.now();\n\n        private final LogEntryType logEntryType;\n\n        private final Event event;\n\n        private final int count;\n\n        public LogEntry(LogEntryType type, Event event) {\n            this.logEntryType = type;\n            this.event = event;\n\n            counter++;\n            count = counter;\n        }\n\n        public int getCounter() {\n            return count;\n        }\n\n        public Object getSource() {\n            return event.getSource().getClass().getSimpleName();\n        }\n\n        public Object getTarget() {\n            return event.getTarget().getClass().getSimpleName();\n        }\n\n        public String getDescription() {\n            return event.toString();\n        }\n\n        public String getTimestamp() {\n            return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)\n                    .format(timestamp);\n        }\n\n        public String getEventType() {\n            return event.getEventType().getName();\n        }\n\n        public LogEntryType getLogEntryType() {\n            return logEntryType;\n        }\n\n        public String getNewStartTime() {\n            if (event instanceof CalendarEvent) {\n                CalendarEvent evt = (CalendarEvent) event;\n                Entry<?> entry = evt.getEntry();\n                if (entry != null) {\n                    return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)\n                            .format(entry.getStartAsLocalDateTime());\n                } else {\n                    return \"\";\n                }\n            } else if (event instanceof RequestEvent) {\n                RequestEvent evt = (RequestEvent) event;\n                if (evt.getDate() != null) {\n                    return DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)\n                            .format(evt.getDate());\n                } else if (evt.getDateTime() != null) {\n                    return DateTimeFormatter\n                            .ofLocalizedDateTime(FormatStyle.SHORT)\n                            .format(evt.getDateTime());\n                } else if (evt.getYearMonth() != null) {\n                    return evt.getYearMonth().toString();\n                } else if (evt.getYearMonth() != null) {\n                    return evt.getYear().toString();\n                }\n            }\n\n            return null;\n        }\n\n        public String getNewEndTime() {\n            if (event instanceof CalendarEvent) {\n                CalendarEvent evt = (CalendarEvent) event;\n                Entry<?> entry = evt.getEntry();\n                if (entry != null) {\n                    return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)\n                            .format(entry.getEndAsLocalDateTime());\n                }\n            }\n\n            return null;\n        }\n\n        public String getOldStartTime() {\n            if (event instanceof CalendarEvent) {\n                CalendarEvent evt = (CalendarEvent) event;\n                Interval oldInterval = evt.getOldInterval();\n                if (oldInterval != null) {\n                    return DateTimeFormatter\n                            .ofLocalizedDateTime(FormatStyle.SHORT)\n                            .format(LocalDateTime.of(oldInterval.getStartDate(),\n                                    oldInterval.getStartTime()));\n                }\n            }\n\n            return null;\n        }\n\n        public String getOldEndTime() {\n            if (event instanceof CalendarEvent) {\n                CalendarEvent evt = (CalendarEvent) event;\n                Interval oldInterval = evt.getOldInterval();\n                if (oldInterval != null) {\n                    return DateTimeFormatter\n                            .ofLocalizedDateTime(FormatStyle.SHORT)\n                            .format(LocalDateTime.of(oldInterval.getEndDate(),\n                                    oldInterval.getEndTime()));\n                }\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/LoadDataSettingsProvider.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport javafx.scene.control.Control;\n\nimport java.time.LocalDate;\nimport java.time.ZoneId;\nimport java.util.List;\n\npublic interface LoadDataSettingsProvider {\n\n    String getLoaderName();\n\n    LocalDate getLoadStartDate();\n\n    LocalDate getLoadEndDate();\n\n    ZoneId getZoneId();\n\n    List<CalendarSource> getCalendarSources();\n\n    Control getControl();\n\n    boolean isCalendarVisible(Calendar calendar);\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/MonthEntryViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.MonthEntryView;\nimport com.calendarfx.view.MonthView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.shape.Circle;\n\nimport java.text.MessageFormat;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.time.format.TextStyle;\nimport java.util.Locale;\nimport java.util.Objects;\n\n@SuppressWarnings(\"javadoc\")\npublic class MonthEntryViewSkin extends SkinBase<MonthEntryView> {\n\n    private final DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n\n    protected Label titleLabel;\n    protected Label timeLabel;\n    protected Circle colorDot;\n\n    public MonthEntryViewSkin(MonthEntryView view) {\n        super(view);\n\n        Entry<?> entry = view.getEntry();\n        if (entry.isRecurrence()) {\n            entry = entry.getRecurrenceSourceEntry();\n        }\n\n        Calendar calendar = entry.getCalendar();\n\n        colorDot = new Circle();\n        colorDot.setRadius(2.5);\n        colorDot.setMouseTransparent(true);\n\n        titleLabel = new Label();\n        titleLabel.setGraphic(colorDot);\n        titleLabel.setMinSize(0, 0);\n        titleLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        titleLabel.setMouseTransparent(true);\n\n        timeLabel = new Label();\n        timeLabel.setMouseTransparent(true);\n        timeLabel.setMaxHeight(Double.MAX_VALUE);\n        timeLabel.setMouseTransparent(true);\n\n        // update in these cases\n        entry.titleProperty().addListener(weakUpdateViewListener);\n        entry.fullDayProperty().addListener(weakUpdateViewListener);\n        entry.intervalProperty().addListener(weakUpdateViewListener);\n        entry.calendarProperty().addListener(weakUpdateViewListener);\n\n        view.positionProperty().addListener(weakUpdateViewListener);\n\n        if (calendar != null) {\n            calendar.styleProperty().addListener(weakUpdateViewListener);\n        }\n\n        getChildren().addAll(titleLabel, timeLabel);\n\n        updateView();\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY,\n            double contentWidth, double contentHeight) {\n        double pw = 0;\n\n        if (contentWidth > 120) {\n            pw = timeLabel.prefWidth(-1);\n            timeLabel.resizeRelocate(snapPositionX(contentX + contentWidth - pw), snapPositionY(contentY), snapSizeX(pw), snapSizeY(contentHeight));\n            titleLabel.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY), snapSizeX(contentWidth - pw), snapSizeY(contentHeight));\n            timeLabel.setVisible(true);\n        } else {\n            titleLabel.resizeRelocate(snapPositionX(contentX), snapPositionY(contentY), snapSizeX(contentWidth - pw), snapSizeY(contentHeight));\n            timeLabel.setVisible(false);\n        }\n    }\n\n    @Override\n    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return Math.max(titleLabel.prefHeight(-1), timeLabel.prefHeight(-1)) + topInset + bottomInset;\n    }\n\n    private final InvalidationListener updateViewListener = it -> updateView();\n\n    private final WeakInvalidationListener weakUpdateViewListener = new WeakInvalidationListener(updateViewListener);\n\n    protected void updateView() {\n        final MonthEntryView view = getSkinnable();\n        Entry<?> entry = view.getEntry();\n        final Calendar calendar = entry.getCalendar();\n\n        if (entry.isRecurrence()) {\n            entry = entry.getRecurrenceSourceEntry();\n        }\n\n        final ObservableList<String> styleClass = view.getStyleClass();\n\n        styleClass.setAll(\"month-entry-view\", \"default-style-entry-small\");\n        if (calendar != null) {\n            styleClass.add(calendar.getStyle() + \"-entry-small\");\n        }\n\n        if (entry.isFullDay() || entry.isMultiDay()) {\n            styleClass.add(\"default-style-entry-small-full-day\");\n            if (calendar != null) {\n                styleClass.add(calendar.getStyle() + \"-entry-small-full-day\");\n            }\n        }\n\n        // color dot visibility\n        colorDot.setVisible(!entry.isFullDay() && !entry.isMultiDay());\n\n        if (calendar != null) {\n            // color dot style\n            colorDot.getStyleClass().setAll(\"default-style-icon-small\", calendar.getStyle() + \"-icon-small\");\n\n            // title style\n            titleLabel.getStyleClass().setAll(\"default-style-entry-small-title-label\", calendar.getStyle() + \"-entry-small-title-label\");\n\n            // time label style\n            timeLabel.getStyleClass().setAll(\"default-style-entry-small-time-label\", calendar.getStyle() + \"-entry-small-time-label\");\n\n            // title style\n            if (entry.isMultiDay() || entry.isFullDay()) {\n                titleLabel.getStyleClass().addAll(\"default-style-entry-small-title-label-full-day\", calendar.getStyle() + \"-entry-small-title-label-full-day\");\n                timeLabel.getStyleClass().addAll(\"default-style-entry-small-time-label-full-day\", calendar.getStyle() + \"-entry-small-time-label-full-day\");\n            }\n        } else {\n            // Calendar might be null when the entry is a \"dummy\" entry.\n            // color dot style\n            colorDot.getStyleClass().setAll(\"default-style-icon-small\");\n\n            // title style\n            titleLabel.getStyleClass().setAll(\"default-style-entry-small-title-label\");\n\n            // time label style\n            timeLabel.getStyleClass().setAll(\"default-style-entry-small-time-label\");\n\n            // title style\n            if (entry.isMultiDay() || entry.isFullDay()) {\n                titleLabel.getStyleClass().addAll(\"default-style-entry-small-title-label-full-day\");\n                timeLabel.getStyleClass().addAll(\"default-style-entry-small-time-label-full-day\");\n            }\n        }\n\n        if (entry.isFullDay()) {\n            timeLabel.setText(\"\");\n        }\n\n        switch (view.getPosition()) {\n        case FIRST:\n        case ONLY:\n            titleLabel.setText(entry.getTitle());\n            if (!(entry.isFullDay() || entry.isMultiDay())) {\n                titleLabel.setGraphic(colorDot);\n            }\n            break;\n        default:\n            // Blank string is important for layout purposes (title and time\n            // might have different font sizes\n            titleLabel.setText(\" \");\n            titleLabel.setGraphic(null);\n            break;\n        }\n\n        styleClass.add(\"default-style-entry-small-\" + view.getPosition().toString().toLowerCase());\n\n        // time label text\n        if (!entry.isFullDay()) {\n            DateTimeFormatter dateTimeFormatter = getDateTimeFormatter();\n            MonthView dateControl = getSkinnable().getDateControl();\n            ZoneId entryZoneId = entry.getZoneId();\n            ZoneId dateControlZoneId = dateControl.getZoneId();\n            if (entry.isMultiDay()) {\n                switch (view.getPosition()) {\n                case LAST:\n                    if (!Objects.equals(entryZoneId, dateControlZoneId)) {\n                        timeLabel.setText(MessageFormat.format(Messages.getString(\"MonthEntryViewSkin.ENDS_AT\"), dateTimeFormatter.format(entry.getEndAsZonedDateTime().withZoneSameInstant(dateControlZoneId).toLocalTime()) + \" (\" + entryZoneId.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + \")\"));\n                    } else {\n                        timeLabel.setText(MessageFormat.format(Messages.getString(\"MonthEntryViewSkin.ENDS_AT\"), dateTimeFormatter.format(entry.getEndTime())));\n                    }\n                    break;\n                case FIRST:\n                    /*\n                     * Only show it if the view is the first and represents the\n                     * start date of the entry. Views can be on first but\n                     * represent a date between start and end date of the entry\n                     * (e.g. when not shown in the first week of the entry time\n                     * interval).\n                     */\n                    if (view.getStartDate().equals(entry.getStartDate())) {\n                        if (!Objects.equals(entryZoneId, dateControlZoneId)) {\n                            timeLabel.setText(dateTimeFormatter.format(entry.getStartAsZonedDateTime().withZoneSameInstant(dateControlZoneId).toLocalTime()) + \" (\" + entryZoneId.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + \")\");\n                        } else {\n                            timeLabel.setText(dateTimeFormatter.format(entry.getStartTime()));\n                        }\n                    }\n                    break;\n                default:\n                    timeLabel.setText(\"\");\n                    break;\n                }\n            } else {\n                if (!Objects.equals(entryZoneId, dateControlZoneId)) {\n                    timeLabel.setText(dateTimeFormatter.format(entry.getStartAsZonedDateTime().withZoneSameInstant(dateControlZoneId).toLocalTime()) + \" (\" + entryZoneId.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + \")\");\n                } else {\n                    timeLabel.setText(dateTimeFormatter.format(entry.getStartTime()));\n                }\n            }\n        }\n\n        styleClass.addAll(entry.getStyleClass());\n    }\n\n    /**\n     * Sets the Date Time Format on the Label that shows the time.\n     * \n     * @return the date time formatter.\n     */\n    protected DateTimeFormatter getDateTimeFormatter() {\n        return formatter;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/MonthSheetViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.DateControl.DateDetailsParameter;\nimport com.calendarfx.view.DateSelectionModel;\nimport com.calendarfx.view.MonthSheetView;\nimport com.calendarfx.view.MonthSheetView.DateCell;\nimport com.calendarfx.view.MonthSheetView.WeekDayLayoutStrategy;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.value.ChangeListener;\nimport javafx.event.EventHandler;\nimport javafx.event.WeakEventHandler;\nimport javafx.geometry.Bounds;\nimport javafx.scene.Node;\nimport javafx.scene.control.Control;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.util.Callback;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Level;\n\npublic class MonthSheetViewSkin extends DateControlSkin<MonthSheetView> implements LoadDataSettingsProvider {\n\n    private final GridPane grid = new GridPane();\n    private final DataLoader dataLoader = new DataLoader(this);\n    private final Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n    private final Map<LocalDate, DateCell> cellMap = new HashMap<>();\n    private final Map<Position, DateCell> positionToDateCellMap = new HashMap<>();\n    private final Map<LocalDate, Position> dateToPositionMap = new HashMap<>();\n\n    public MonthSheetViewSkin(MonthSheetView control) {\n        super(control);\n\n        grid.getStyleClass().add(\"container\");\n        grid.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        updateRowConstraints();\n        control.weekDayLayoutProperty().addListener(it -> updateRowConstraints());\n\n        InvalidationListener builder = obs -> buildCells();\n        control.dateProperty().addListener(builder);\n        control.viewUnitProperty().addListener(builder);\n        control.extendedViewUnitProperty().addListener(builder);\n        control.extendedUnitsForwardProperty().addListener(builder);\n        control.extendedUnitsBackwardProperty().addListener(builder);\n        control.weekDayLayoutProperty().addListener(builder);\n        control.weekFieldsProperty().addListener(builder);\n        control.cellFactoryProperty().addListener(builder);\n        control.headerCellFactoryProperty().addListener(builder);\n        control.enableHyperlinksProperty().addListener(builder);\n        control.getCalendars().addListener((Observable obs) -> updateEntries(\"list of calendars changed\"));\n        control.clickBehaviourProperty().addListener(it -> control.getDateSelectionModel().clear());\n        control.getDateSelectionModel().getSelectedDates().addListener((Observable obs) -> updateSelected());\n\n        // important to use change listener\n        ChangeListener todayUpdater = (obs, oldValue, newValue) -> updateToday();\n        control.todayProperty().addListener(todayUpdater);\n        control.showTodayProperty().addListener(todayUpdater);\n\n        EventHandler<KeyEvent> keyPressedHandler = evt -> {\n            DateSelectionModel selectionModel = getSkinnable().getDateSelectionModel();\n            LocalDate lastSelected = selectionModel.getLastSelected();\n\n            if (lastSelected != null) {\n\n                Position lastPosition = dateToPositionMap.get(lastSelected);\n\n                LocalDate newSelection = null;\n                boolean isNavigationKey = true;\n\n                switch (evt.getCode()) {\n                    case UP:\n                        newSelection = lastSelected.plusDays(-1);\n                        break;\n\n                    case DOWN:\n                        newSelection = lastSelected.plusDays(1);\n                        break;\n\n                    case LEFT:\n                        Position newPosition = new Position(Math.max(0, lastPosition.getColumn() - 1), lastPosition.getRow());\n                        DateCell newCell = positionToDateCellMap.get(newPosition);\n                        if (newCell != null) {\n                            newSelection = newCell.getDate();\n                        }\n                        break;\n\n                    case RIGHT:\n                        newPosition = new Position(lastPosition.getColumn() + 1, lastPosition.getRow());\n                        newCell = positionToDateCellMap.get(newPosition);\n                        if (newCell != null) {\n                            newSelection = newCell.getDate();\n                        }\n                        break;\n\n                    default:\n                        isNavigationKey = false;\n                        break;\n                }\n\n                if (getSkinnable().isVisibleDate(newSelection)) {\n                    if (evt.isShiftDown()) {\n                        selectionModel.selectUntil(newSelection);\n                    } else if (evt.isShortcutDown()) {\n                        selectionModel.select(newSelection);\n                    } else {\n                        selectionModel.clearAndSelect(newSelection);\n                    }\n                }\n\n                if (isNavigationKey) {\n                    evt.consume();\n                }\n            }\n        };\n        control.addEventFilter(KeyEvent.KEY_PRESSED, keyPressedHandler);\n        control.setFocusTraversable(true);\n\n        buildCells();\n        getChildren().add(grid);\n\n        updateEntries(\"initial load\");\n        updateToday();\n    }\n\n    private void updateRowConstraints() {\n        int rowCount = 32; // header + 31 days\n        if (getSkinnable().getWeekDayLayout() == WeekDayLayoutStrategy.ALIGNED) {\n            rowCount += 6; // 6 = max number of empty slots / cells at the top\n        }\n\n        List<RowConstraints> rowConstraints = new ArrayList<>();\n        for (int i = 0; i <= rowCount; i++) {\n            RowConstraints con = new RowConstraints();\n            con.setFillHeight(true);\n            con.setPrefHeight(Region.USE_COMPUTED_SIZE);\n            con.setMinHeight(Region.USE_PREF_SIZE);\n            con.setMaxHeight(Double.MAX_VALUE);\n            con.setVgrow(i == 0 ? Priority.NEVER : Priority.ALWAYS);\n            rowConstraints.add(con);\n        }\n\n        grid.getRowConstraints().setAll(rowConstraints);\n    }\n\n    @Override\n    protected void calendarVisibilityChanged() {\n        updateEntries(\"calendar visibility changed\");\n    }\n\n    private void buildCells() {\n        positionToDateCellMap.clear();\n        dateToPositionMap.clear();\n        cellMap.clear();\n\n        YearMonth start = getSkinnable().getExtendedStartMonth();\n        YearMonth end = getSkinnable().getExtendedEndMonth();\n\n        int colIndex = 0;\n\n        grid.getColumnConstraints().clear();\n        grid.getChildren().clear();\n\n        while (!start.isAfter(end)) {\n            ColumnConstraints columnConstraints = new ColumnConstraints();\n            columnConstraints.setFillWidth(true);\n            columnConstraints.setMinWidth(Region.USE_PREF_SIZE);\n            columnConstraints.setMaxWidth(Double.MAX_VALUE);\n            grid.getColumnConstraints().add(columnConstraints);\n\n            buildCells(start, colIndex);\n\n            start = start.plusMonths(1);\n            colIndex++;\n        }\n\n        grid.getColumnConstraints().forEach(con -> con.setPercentWidth(100d / (double) grid.getColumnConstraints().size()));\n\n        updateEntries(\"cells were rebuild\");\n        updateToday();\n        updateSelected();\n    }\n\n    private void buildCells(YearMonth yearMonth, int colIndex) {\n        List<Node> cells = new ArrayList<>();\n        Node header = buildHeaderCell(yearMonth);\n        header.getStyleClass().add(\"month-header\");\n\n        cells.add(header);\n\n        LocalDate start = yearMonth.atDay(1);\n        LocalDate end = yearMonth.atEndOfMonth();\n\n        if (getSkinnable().getWeekDayLayout() == WeekDayLayoutStrategy.ALIGNED) {\n            DayOfWeek firstDayOfWeek = getSkinnable().getFirstDayOfWeek();\n            DayOfWeek startDayOfWeek = start.getDayOfWeek();\n            int distanceDays = Math.abs(firstDayOfWeek.getValue() - startDayOfWeek.getValue());\n\n            while (distanceDays-- > 0) {\n                cells.add(buildCell(null));\n            }\n        }\n\n        while (start.isBefore(end) || start.isEqual(end)) {\n            cells.add(buildCell(start));\n            start = start.plusDays(1);\n        }\n\n        buildEmptyCellBottom(cells);\n\n        final YearMonth extendedStart = getSkinnable().getExtendedStartMonth();\n        final YearMonth extendedEnd = getSkinnable().getExtendedEndMonth();\n\n        cells.forEach(cell -> {\n            if (extendedStart.equals(yearMonth)) {\n                cell.getStyleClass().add(\"first-month\");\n            } else if (extendedEnd.equals(yearMonth)) {\n                cell.getStyleClass().add(\"last-month\");\n            } else {\n                cell.getStyleClass().add(\"middle-month\");\n            }\n        });\n\n        for (int i = 0; i < cells.size(); i++) {\n            Node node = cells.get(i);\n            grid.add(node, colIndex, i + 1);\n\n            if (node instanceof DateCell) {\n\n                final Position position = new Position(colIndex, i);\n                final DateCell dateCell = (DateCell) node;\n                final LocalDate date = dateCell.getDate();\n\n                cellMap.put(date, dateCell);\n                positionToDateCellMap.put(position, dateCell);\n                dateToPositionMap.put(date, position);\n            }\n        }\n    }\n\n    private DateCell buildCell(LocalDate date) {\n        DateCell cell = getSkinnable().getCellFactory().call(new MonthSheetView.DateParameter(getSkinnable(), date));\n        cell.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        cell.setOnMouseClicked(weakCellClickedHandler);\n        return cell;\n    }\n\n    private Node buildHeaderCell(YearMonth yearMonth) {\n        return getSkinnable().getHeaderCellFactory().\n                call(new MonthSheetView.HeaderParameter(getSkinnable(), yearMonth));\n    }\n\n    private void buildEmptyCellBottom(List<Node> cells) {\n        int maximumCells = 31;\n        if (getSkinnable().getWeekDayLayout().equals(WeekDayLayoutStrategy.ALIGNED)) {\n            maximumCells = 37;\n        }\n\n        int cellsNumber = cells.size() - 1;\n\n        if (cellsNumber < maximumCells) {\n            while (cellsNumber < maximumCells) {\n                cells.add(buildCell(null));\n                cellsNumber++;\n            }\n        }\n    }\n\n    private void updateSelected() {\n        List<LocalDate> selectedDates = getSkinnable().getDateSelectionModel().getSelectedDates();\n        grid.getChildren().stream()\n                .filter(child -> child instanceof DateCell)\n                .map(child -> (DateCell) child)\n                .forEach(cell -> cell.setSelected(selectedDates.contains(cell.getDate())));\n    }\n\n    private void updateToday() {\n        LocalDate today = getSkinnable().getToday();\n        grid.getChildren().stream()\n                .filter(child -> child instanceof DateCell)\n                .map(child -> (DateCell) child)\n                .forEach(cell -> cell.setToday(getSkinnable().isShowToday() && today.equals(cell.getDate())));\n    }\n\n    private static final class Position {\n\n        int column;\n        int row;\n\n        private Position(int column, int row) {\n            this.column = column;\n            this.row = row;\n        }\n\n        public int getColumn() {\n            return column;\n        }\n\n        public int getRow() {\n            return row;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) {\n                return true;\n            }\n\n            if (o == null || getClass() != o.getClass()) {\n                return false;\n            }\n\n            Position position = (Position) o;\n\n            if (column != position.column) {\n                return false;\n            }\n\n            return row == position.row;\n        }\n\n        @Override\n        public int hashCode() {\n            int result = column;\n            result = 31 * result + row;\n            return result;\n        }\n    }\n\n    private final EventHandler<MouseEvent> cellClickedHandler = evt -> {\n        if (!(evt.getSource() instanceof DateCell)) {\n            return;\n        }\n\n        DateCell cell = (DateCell) evt.getSource();\n        cell.requestFocus();\n        LocalDate date = cell.getDate();\n\n        if (date != null) {\n            switch (getSkinnable().getClickBehaviour()) {\n                case NONE:\n                    break;\n                case PERFORM_SELECTION:\n                    DateSelectionModel selectionModel = getSkinnable().getDateSelectionModel();\n\n                    if (selectionModel.isSelected(date)) {\n                        /*\n                         * Only deselect if the user uses the left / the primary button.\n                         * The right button for the context menu will not deselect the cell.\n                         */\n                        if (evt.getButton() == MouseButton.PRIMARY) {\n                            if (evt.isShortcutDown()) {\n                                selectionModel.deselect(date);\n                            } else {\n                                selectionModel.clear();\n                            }\n                        }\n                    } else {\n                        if (evt.isShiftDown() && evt.getButton() == MouseButton.PRIMARY) {\n                            selectionModel.selectUntil(date);\n                        } else if (evt.isShortcutDown() && evt.getButton() == MouseButton.PRIMARY) {\n                            selectionModel.select(date);\n                        } else {\n                            selectionModel.clearAndSelect(date);\n                        }\n                    }\n                    break;\n                case SHOW_DETAILS:\n                    showDateDetails(date);\n                    break;\n            }\n        }\n    };\n\n    private void showDateDetails(LocalDate date) {\n        DateCell cell = cellMap.get(date);\n        Bounds bounds = cell.localToScreen(cell.getLayoutBounds());\n        Callback<DateDetailsParameter, Boolean> callback = getSkinnable().getDateDetailsCallback();\n        if (callback != null) {\n            callback.call(new DateDetailsParameter(null, getSkinnable(), cell, cell.getScene().getRoot(), date, bounds.getMinX(), bounds.getMinY()));\n        }\n    }\n\n    private final WeakEventHandler<MouseEvent> weakCellClickedHandler = new WeakEventHandler<>(cellClickedHandler);\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        updateEntries(\"calendar changed\");\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        updateEntries(\"entry calendar changed\");\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        updateEntries(\"entry interval changed\");\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n        updateEntries(\"entry full day flag changed\");\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        updateEntries(\"entry recurrence rule changed\");\n    }\n\n    private void updateEntries(String reason) {\n        if (LoggingDomain.VIEW.isLoggable(Level.FINE)) {\n            LoggingDomain.VIEW.fine(\"updating entries because: \" + reason);\n        }\n\n        dataMap.clear();\n        dataLoader.loadEntries(dataMap);\n\n        for (LocalDate date : cellMap.keySet()) {\n            List<Entry<?>> entries = dataMap.get(date);\n            DateCell cell = cellMap.get(date);\n            cell.updateEntries(entries == null ? Collections.emptyList() : entries);\n        }\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"Month Sheet View\";\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        return getSkinnable().getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        return getSkinnable().getExtendedStartMonth().atDay(1);\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        return getSkinnable().getExtendedEndMonth().atEndOfMonth();\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/MonthViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.EntryViewBase.Position;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.MonthEntryView;\nimport com.calendarfx.view.MonthView;\nimport com.calendarfx.view.RequestEvent;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.application.Platform;\nimport javafx.beans.Observable;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.css.PseudoClass;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Control;\nimport javafx.scene.control.Label;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.shape.Rectangle;\nimport javafx.util.Callback;\n\nimport java.text.MessageFormat;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.TextStyle;\nimport java.time.temporal.TemporalAdjusters;\nimport java.time.temporal.WeekFields;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static java.lang.Double.MAX_VALUE;\nimport static javafx.scene.layout.Priority.ALWAYS;\n\npublic class MonthViewSkin extends DateControlSkin<MonthView> implements LoadDataSettingsProvider {\n\n    private static final String DAY_OF_WEEK_LABEL = \"day-of-week-label\";\n\n    private static final String DAY_OF_WEEKEND_LABEL = \"day-of-weekend-label\";\n\n    private final GridPane gridPane;\n\n    private final Map<LocalDate, MonthDayView> controlsMap = new HashMap<>();\n\n    private final DataLoader dataLoader;\n\n    private YearMonth displayedYearMonth;\n\n    public MonthViewSkin(MonthView view) {\n        super(view);\n\n        gridPane = new GridPane();\n        gridPane.setAlignment(Pos.CENTER);\n        gridPane.setMinSize(0, 0);\n        gridPane.setMaxSize(MAX_VALUE, MAX_VALUE);\n\n        ColumnConstraints colCon = new ColumnConstraints();\n        colCon.setPercentWidth(100d / 7d);\n\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n        gridPane.getColumnConstraints().add(colCon);\n\n        RowConstraints rowHeaderCon = new RowConstraints();\n        rowHeaderCon.setMinHeight(Region.USE_PREF_SIZE);\n        gridPane.getRowConstraints().add(rowHeaderCon);\n\n        for (int i = 0; i < 6; i++) {\n            RowConstraints rowCon = new RowConstraints();\n            gridPane.getRowConstraints().add(rowCon);\n        }\n\n        gridPane.getStyleClass().add(\"container\");\n\n        ChangeListener updateViewListener = (obs, oldV, newV) -> {\n            System.out.println(\"property: \" + obs.toString());\n            updateView();\n        };\n\n        view.yearMonthProperty().addListener(it -> {\n            if (displayedYearMonth == null || !(displayedYearMonth.equals(view.getYearMonth()))) {\n                updateView();\n            }\n        });\n\n        view.todayProperty().addListener(updateViewListener);\n        view.showWeekNumbersProperty().addListener(updateViewListener);\n        view.showWeekendsProperty().addListener(updateViewListener);\n        view.showWeekdaysProperty().addListener(updateViewListener);\n        view.showTodayProperty().addListener(updateViewListener);\n        view.showCurrentWeekProperty().addListener(updateViewListener);\n        view.weekFieldsProperty().addListener(updateViewListener);\n        view.showTimedEntriesProperty().addListener(updateViewListener);\n        view.showFullDayEntriesProperty().addListener(updateViewListener);\n        view.enableHyperlinksProperty().addListener(updateViewListener);\n\n        dataLoader = new DataLoader(this);\n\n        updateView();\n\n        view.getSelections().addListener((Observable it) -> updateSelection());\n\n        getChildren().add(gridPane);\n\n        view.getSelectedDates().addListener((Observable observable) -> updateDaySelection());\n\n        view.getCalendars().addListener((Observable obs) -> updateEntries(\"list of calendars changed\"));\n        view.suspendUpdatesProperty().addListener(it -> updateEntries(\"suspend updates set to false\"));\n    }\n\n    @Override\n    protected void calendarVisibilityChanged() {\n        updateEntries(\"calendar visibility changed\");\n    }\n\n    @Override\n    protected void refreshData() {\n        updateView();\n    }\n\n    @Override\n    protected void zoneIdChanged() {\n        updateEntries(\"time zone changed\");\n    }\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        updateEntries(\"changes in calendar \" + calendar.getName());\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        if (evt.isEntryAdded()) {\n            updateEntries(\"entry added\");\n        } else if (evt.isEntryRemoved()) {\n            updateEntries(\"entry removed\");\n        }\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n        // full day changes will be handled by the entry views\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        /*\n         * We can hardly figure out whether the rule change has an impact\n         * on the month view. Maybe the old rule did ... who knows.\n         */\n        updateEntries(\"recurrence rule changed\");\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        Entry<?> entry = evt.getEntry();\n\n        if (isRelevant(evt.getOldInterval()) || isRelevant(entry)) {\n            updateEntries(\"entry interval changed\");\n        }\n    }\n\n    private final PseudoClass selectedClass = PseudoClass.getPseudoClass(\"selected\");\n\n    private void updateDaySelection() {\n        for (MonthDayView view : controlsMap.values()) {\n            view.pseudoClassStateChanged(selectedClass, false);\n        }\n\n        for (LocalDate date : getSkinnable().getSelectedDates()) {\n            MonthDayView view = controlsMap.get(date);\n            if (view != null) {\n                view.pseudoClassStateChanged(selectedClass, true);\n            }\n        }\n    }\n\n    private void updateSelection() {\n        Set<Object> selectedKeys = getSkinnable().getSelections().stream().map(Entry::getId).collect(Collectors.toSet());\n\n        for (MonthDayView view : controlsMap.values()) {\n            MonthDayEntriesPane entriesPane = view.getEntriesPane();\n            for (Node node : entriesPane.getChildren()) {\n                if (node instanceof MonthEntryView) {\n                    MonthEntryView entryView = (MonthEntryView) node;\n                    Entry<?> entry = entryView.getEntry();\n                    if (entry != null) {\n                        Object entryId = entryView.getEntry().getId();\n                        entryView.getProperties().put(\"selected\",\n                                selectedKeys.contains(entryId));\n                    }\n                }\n            }\n        }\n    }\n\n    private void updateView() {\n        controlsMap.clear();\n\n        MonthView view = getSkinnable();\n        gridPane.getChildren().clear();\n\n        displayedYearMonth = view.getYearMonth();\n\n        WeekFields weekFields = view.getWeekFields();\n        DayOfWeek dayOfWeek = weekFields.getFirstDayOfWeek();\n\n        if (view.isShowWeekdays()) {\n            for (int i = 0; i < 7; i++) {\n                // TODO: provide a factory for these labels\n                Label dayOfWeekLabel = new Label(dayOfWeek.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault()));\n                dayOfWeekLabel.setAlignment(Pos.CENTER_RIGHT);\n                dayOfWeekLabel.setMaxSize(MAX_VALUE, MAX_VALUE);\n                dayOfWeekLabel.getStyleClass().add(DAY_OF_WEEK_LABEL);\n\n                if (view.isShowWeekends() && view.getWeekendDays().contains(dayOfWeek)) {\n                    dayOfWeekLabel.getStyleClass().add(DAY_OF_WEEKEND_LABEL);\n                }\n\n                GridPane.setHgrow(dayOfWeekLabel, ALWAYS);\n                gridPane.add(dayOfWeekLabel, i, 0);\n                dayOfWeek = dayOfWeek.plus(1);\n            }\n        }\n\n        LocalDate date = view.getDate().with(TemporalAdjusters.firstDayOfMonth());\n\n        date = Util.adjustToFirstDayOfWeek(date, getSkinnable().getFirstDayOfWeek());\n\n        final int firstWeek = 0;\n        final int lastWeek = 5;\n        final int firstDay = 0;\n        final int lastDay = 6;\n\n        for (int week = firstWeek; week <= lastWeek; week++) {\n            for (int day = firstDay; day <= lastDay; day++) {\n                // TODO: this should be done via a factory (cell factory already defined on MonthViewBase)\n                MonthDayView dayOfMonthLabel = new MonthDayView(date, week, day);\n                if (week == firstWeek) {\n                    dayOfMonthLabel.getStyleClass().add(\"first-week\");\n                } else if (week == lastWeek) {\n                    dayOfMonthLabel.getStyleClass().add(\"last-week\");\n                } else {\n                    dayOfMonthLabel.getStyleClass().add(\"middle-week\");\n                }\n\n                if (day == firstDay) {\n                    dayOfMonthLabel.getStyleClass().add(\"first-day\");\n                } else if (day == lastDay) {\n                    dayOfMonthLabel.getStyleClass().add(\"last-day\");\n                } else {\n                    dayOfMonthLabel.getStyleClass().add(\"middle-day\");\n                }\n\n                controlsMap.put(date, dayOfMonthLabel);\n                GridPane.setHgrow(dayOfMonthLabel, ALWAYS);\n                GridPane.setVgrow(dayOfMonthLabel, ALWAYS);\n                gridPane.add(dayOfMonthLabel, day, week + 1);\n                date = date.plusDays(1);\n            }\n        }\n\n        updateDaySelection();\n        updateEntries(\"view was updated after a view property change\");\n    }\n\n    private final List<Map<Object, Integer>> positionMaps = new ArrayList<>();\n\n    private int[][] numberOfFullDayEntries;\n\n    private void updateEntries(String reason) {\n        if (getSkinnable().isSuspendUpdates()) {\n            return;\n        }\n\n        Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n        dataLoader.loadEntries(dataMap);\n\n        positionMaps.clear();\n\n        LocalDate date = getLoadStartDate();\n\n        int numberOfWeeks = 6;\n        int numberOfDays = 7;\n\n        numberOfFullDayEntries = new int[numberOfWeeks][numberOfDays];\n\n        for (int week = 0; week < numberOfWeeks; week++) {\n            final int w = week;\n            for (int day = 0; day < numberOfDays; day++) {\n\n                final int d = day;\n\n                List<Entry<?>> entries = dataMap.get(date);\n                if (entries != null) {\n                    entries.forEach(entry -> {\n                        if (entry.isFullDay() || entry.isMultiDay()) {\n                            numberOfFullDayEntries[w][d]++;\n                        }\n                    });\n                }\n\n                date = date.plusDays(1);\n            }\n        }\n\n        // reset date\n        date = getLoadStartDate();\n\n        for (int week = 0; week < numberOfWeeks; week++) {\n\n            final Map<Object, Integer> keyPositionMap = new HashMap<>();\n            positionMaps.add(keyPositionMap);\n\n            for (int day = 0; day < numberOfDays; day++) {\n                List<Entry<?>> entries = dataMap.get(date);\n                if (entries != null) {\n                    entries.forEach(entry -> {\n                        if (entry.isFullDay() || entry.isMultiDay()) {\n                            Object entryId = entry.getId();\n                            if (keyPositionMap.get(entryId) == null) {\n                                int position = 0;\n                                for (Entry<?> otherEntry : entries) {\n                                    if (otherEntry.isFullDay() || otherEntry.isMultiDay()) {\n                                        Object otherEntryId = otherEntry.getId();\n                                        if (!otherEntryId.equals(entryId)) {\n                                            if (keyPositionMap.get(otherEntryId) != null) {\n                                                int otherPosition = keyPositionMap.get(otherEntryId);\n                                                if (otherPosition == position) {\n                                                    position = otherPosition + 1;\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                                keyPositionMap.put(entryId, position);\n                            }\n                        }\n                    });\n                }\n\n                date = date.plusDays(1);\n            }\n        }\n\n        // clear all entries\n        for (MonthDayView view : controlsMap.values()) {\n            MonthDayEntriesPane entriesPane = view.getEntriesPane();\n            entriesPane.getEntries().clear();\n        }\n\n        // add the new entries\n        for (LocalDate localDate : dataMap.keySet()) {\n\n            if (controlsMap.containsKey(localDate)) {\n                MonthDayView view = controlsMap.get(localDate);\n                MonthDayEntriesPane entriesPane = view.getEntriesPane();\n                List<Entry<?>> entries = dataMap.get(localDate);\n                if (entries != null) {\n\n                    if (!getSkinnable().isShowFullDayEntries()) {\n                        entries.removeIf(Entry::isFullDay);\n                    }\n\n                    if (!getSkinnable().isShowTimedEntries()) {\n                        entries.removeIf(entry -> !entry.isFullDay());\n                    }\n\n                    Collections.sort(entries);\n                    entriesPane.getEntries().setAll(entries);\n                }\n            }\n        }\n\n        LoggingDomain.VIEW.fine(\"updated entries in month view \" + getSkinnable().getYearMonth() + \": reason = \" + reason);\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"Month View\";\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        /*\n         * The month view also shows the last couple of days of the previous\n         * month.\n         */\n        return Util.adjustToFirstDayOfWeek(getSkinnable().getDate().withDayOfMonth(1), getSkinnable().getFirstDayOfWeek());\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        /*\n         * The month view also shows the first couple of days of the next month.\n         */\n        return getLoadStartDate().plusDays(41); // the view always shows 41 month days\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        return getSkinnable().getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n\n    class MonthDayView extends VBox {\n\n        private static final String LAST_DAY_OF_WEEK = \"last-day-of-week\";\n        private static final String FIRST_DAY_OF_WEEK = \"first-day-of-week\";\n\n        private static final String MONTH_DAY = \"day\";\n        private static final String MONTH_DAY_HEADER = \"header\";\n        private static final String MONTH_DAY_ENTRIES_PANE = \"entries-pane\";\n        private static final String MONTH_DAY_TODAY = \"today\";\n        private static final String MONTH_DAY_CURRENT_WEEK = \"current-week\";\n\n        private static final String WEEKEND_DAY = \"weekend-day\";\n        private static final String DAY_NOT_OF_MONTH_LABEL = \"day-not-of-month-label\";\n        private static final String DAY_OF_MONTH_LABEL = \"day-of-month-label\";\n        private static final String TODAY_LABEL = \"today-label\";\n        private static final String WEEK_OF_YEAR_LABEL = \"week-of-year-label\";\n        private static final String CURRENT_WEEK_OF_YEAR_LABEL = \"current-week-of-year-label\";\n\n        private final MonthDayEntriesPane entriesPane;\n\n        private final LocalDate date;\n\n        MonthDayView(LocalDate date, int week, int day) {\n            this.date = date;\n\n            getStyleClass().add(MONTH_DAY);\n\n            setFillWidth(true);\n\n            if (day == 0) {\n                getStyleClass().add(FIRST_DAY_OF_WEEK);\n            } else if (day == 6) {\n                getStyleClass().add(LAST_DAY_OF_WEEK);\n            }\n\n            Label dateLabel = new Label();\n            dateLabel.setMaxWidth(MAX_VALUE);\n\n            if (getSkinnable().isEnableHyperlinks()) {\n                dateLabel.setOnMouseClicked(evt -> {\n                    if (evt.getButton() == MouseButton.PRIMARY && evt.getClickCount() == 1) {\n                        fireEvent(new RequestEvent(getSkinnable(), getSkinnable(), date));\n                    }\n                });\n                dateLabel.getStyleClass().add(\"date-hyperlink\");\n            }\n\n            WeekFields weekFields = getSkinnable().getWeekFields();\n\n            LocalDate firstDay = Util.adjustToFirstDayOfWeek(date, getSkinnable().getFirstDayOfWeek());\n\n            final int weekOfYear = firstDay.get(weekFields.weekOfYear());\n            final Year year = Year.of(Util.adjustToFirstDayOfWeek(date, getSkinnable().getFirstDayOfWeek()).getYear());\n\n            Label weekLabel = new Label(Integer.toString(weekOfYear));\n            weekLabel.setVisible(firstDay.equals(date));\n\n            if (getSkinnable().isEnableHyperlinks()) {\n                weekLabel.getStyleClass().add(\"date-hyperlink\");\n                weekLabel.setOnMouseClicked(evt -> {\n                    if (evt.getClickCount() == 1) {\n                        getSkinnable().fireEvent(new RequestEvent(getSkinnable(), getSkinnable(), year, weekOfYear));\n                    }\n                });\n            }\n\n            MonthView monthView = getSkinnable();\n\n            if (getSkinnable().isShowCurrentWeek() && date.getYear() == monthView.getToday().getYear()\n                    && weekOfYear == monthView.getToday().get(weekFields.weekOfYear())) {\n                dateLabel.setText(DateTimeFormatter.ofPattern(Messages.getString(\"MonthViewSkin.TODAY_DATE_FORMAT\")).format(date));\n                weekLabel.getStyleClass().add(CURRENT_WEEK_OF_YEAR_LABEL);\n                getStyleClass().add(MONTH_DAY_CURRENT_WEEK);\n            } else {\n                dateLabel.setText(Integer.toString(date.getDayOfMonth()));\n                weekLabel.getStyleClass().add(WEEK_OF_YEAR_LABEL);\n            }\n\n            if (YearMonth.from(date).equals(monthView.getYearMonth())) {\n                dateLabel.getStyleClass().add(DAY_OF_MONTH_LABEL);\n                if (date.equals(monthView.getToday())) {\n                    if (monthView.isShowToday()) {\n                        dateLabel.getStyleClass().add(TODAY_LABEL);\n                        getStyleClass().add(MONTH_DAY_TODAY);\n                    }\n                }\n            } else {\n                dateLabel.getStyleClass().add(DAY_NOT_OF_MONTH_LABEL);\n            }\n\n            DayOfWeek dayOfWeek = date.getDayOfWeek();\n\n            if (monthView.isShowWeekends() && monthView.getWeekendDays().contains(dayOfWeek)) {\n                getStyleClass().add(WEEKEND_DAY);\n                dateLabel.getStyleClass().add(WEEKEND_DAY);\n            }\n\n            getStyleClass().add(dayOfWeek.toString().toLowerCase());\n\n            BorderPane headerPane = new BorderPane();\n            headerPane.getStyleClass().add(MONTH_DAY_HEADER);\n            if (monthView.isShowWeekNumbers()) {\n                headerPane.setLeft(weekLabel);\n            }\n            headerPane.setRight(dateLabel);\n\n            VBox.setVgrow(headerPane, Priority.NEVER);\n            getChildren().add(headerPane);\n\n            entriesPane = new MonthDayEntriesPane(date, week, day);\n            entriesPane.getStyleClass().add(MONTH_DAY_ENTRIES_PANE);\n            VBox.setVgrow(entriesPane, Priority.ALWAYS);\n            getChildren().add(entriesPane);\n\n            setMaxSize(MAX_VALUE, MAX_VALUE);\n            setPrefSize(50, 50);\n\n            addEventHandler(MouseEvent.MOUSE_CLICKED, evt -> {\n                if (evt.getClickCount() == 1 && evt.getButton() == MouseButton.PRIMARY ) {\n                    // ENTRY selection\n                    monthView.getSelections().clear();\n\n                    // DATE selection\n                    boolean wasSelected = monthView.getSelectedDates().contains(date);\n\n                    if (!evt.isShiftDown() && !evt.isControlDown()) {\n                        monthView.getSelectedDates().clear();\n                    }\n\n                    if (!(wasSelected || getSkinnable().getSelectedDates().contains(date))) {\n                        monthView.getSelectedDates().add(date);\n                    }\n                }\n            });\n        }\n\n        final MonthDayEntriesPane getEntriesPane() {\n            return entriesPane;\n        }\n\n        final LocalDate getDate() {\n            return date;\n        }\n    }\n\n    class MonthDayEntriesPane extends Pane {\n\n        private static final String MONTH_DAY_MORE_LABEL = \"more-label\";\n        private static final String SPACE = \" \";\n\n        private final Label moreLabel;\n        private final LocalDate date;\n        private final int week;\n        private final int day;\n\n        MonthDayEntriesPane(LocalDate date, int week, int day) {\n            getStyleClass().add(\"entries-pane\");\n\n            this.date = date;\n            this.week = week;\n            this.day = day;\n\n            // since JavaFX 19 this needs to be run later\n            entries.addListener((Observable evt) -> Platform.runLater(() -> update()));\n\n            setMinSize(0, 0);\n            setPrefSize(0, 0);\n\n            Rectangle clip = new Rectangle();\n            clip.widthProperty().bind(widthProperty());\n            clip.heightProperty().bind(heightProperty());\n            setClip(clip);\n\n            moreLabel = new Label();\n            moreLabel.getStyleClass().add(MONTH_DAY_MORE_LABEL);\n            moreLabel.setManaged(false);\n            moreLabel.setVisible(false);\n\n            if (getSkinnable().isEnableHyperlinks()) {\n                moreLabel.getStyleClass().add(\"date-hyperlink\");\n                moreLabel.setOnMouseClicked(evt -> fireEvent(new RequestEvent(this, this, date)));\n            }\n\n            getChildren().add(moreLabel);\n        }\n\n        private final ObservableList<Entry<?>> entries = FXCollections.observableArrayList();\n\n        public final ObservableList<Entry<?>> getEntries() {\n            return entries;\n        }\n\n        private void update() {\n            Util.removeChildren(this, node -> node instanceof MonthEntryView);\n\n            if (!entries.isEmpty()) {\n\n                List<Entry<?>> otherEntries = new ArrayList<>();\n\n                Map<Object, Integer> positionMap = Objects.requireNonNull(positionMaps.get(week), \"missing position map for week number \" + week);\n\n                int maxPosition = -1;\n\n                for (Entry<?> entry : entries) {\n                    Objects.requireNonNull(entry, \"found NULL calendar entry in entry list\");\n                    if (entry.isFullDay() || entry.isMultiDay()) {\n                        int position = positionMap.get(Objects.requireNonNull(entry.getId(), \"entry ID is missing, entry type = \" + entry.getClass().getName() + \", entry title = \" + entry.getTitle()));\n                        maxPosition = Math.max(maxPosition, position);\n                    } else {\n                        otherEntries.add(entry);\n                    }\n                }\n\n                if (maxPosition > -1) {\n                    Node[] fullDayNodes = new Node[maxPosition + 1];\n                    for (int i = 0; i < maxPosition; i++) {\n                        /*\n                         * Do not use factory for this. SPACE is important to guarantee that the blank\n                         * entries have the same height as the regular entries.\n                         */\n                        MonthEntryView label = new MonthEntryView(new Entry<>(SPACE));\n                        label.setVisible(false);\n                        label.getProperties().put(\"control\", getSkinnable());\n                        fullDayNodes[i] = label;\n                    }\n\n                    for (Entry<?> entry : entries) {\n                        if (entry.isFullDay() || entry.isMultiDay()) {\n                            int position = positionMap.get(entry.getId());\n                            fullDayNodes[position] = createNode(entry);\n                        }\n                    }\n\n                    for (Node node : fullDayNodes) {\n                        getChildren().add(node);\n                    }\n                }\n\n                /*\n                 * Individual calendars are already sorted, but now we are\n                 * displaying entries from several calendars, so let's resort.\n                 */\n                Collections.sort(otherEntries);\n\n                for (Entry<?> entry : otherEntries) {\n                    getChildren().add(createNode(entry));\n                }\n            }\n        }\n\n        private Node createNode(Entry<?> entry) {\n            Callback<Entry<?>, MonthEntryView> factory = getSkinnable().getEntryViewFactory();\n            MonthEntryView view = factory.call(entry);\n            view.getProperties().put(\"control\", getSkinnable());\n            view.getProperties().put(\"startDate\", date);\n            view.getProperties().put(\"endDate\", date);\n\n            Position position = Position.ONLY;\n\n            if (entry.isFullDay()) {\n                if (date.isBefore(entry.getEndDate()) && (day == 0 || (date.equals(entry.getStartDate())))) {\n                    position = Position.FIRST;\n                } else if (entry.isMultiDay() && date.equals(entry.getEndDate())) {\n                    position = Position.LAST;\n                } else if (entry.isMultiDay()) {\n                    position = Position.MIDDLE;\n                }\n            } else if (entry.isMultiDay()) {\n                if (day == 0 || (date.equals(entry.getStartDate()) && date.isBefore(entry.getEndDate()))) {\n                    position = Position.FIRST;\n                } else if (date.equals(entry.getEndDate())) {\n                    position = Position.LAST;\n                } else {\n                    position = Position.MIDDLE;\n                }\n            }\n\n            view.getProperties().put(\"position\", position);\n\n            return view;\n        }\n\n        @Override\n        protected void layoutChildren() {\n            Insets insets = getInsets();\n            double w = getWidth();\n            double h = getHeight();\n            double y = insets.getTop();\n\n            moreLabel.setVisible(false);\n\n            List<Node> children = getChildren();\n\n            boolean conflictFound = false;\n            int childrenAdded = 0;\n\n            for (int i = 0; i < children.size(); i++) {\n                Node child = children.get(i);\n                if (child == moreLabel) {\n                    continue;\n                }\n                double ph = child.prefHeight(-1);\n                if (y + ph < h - insets.getTop() - insets.getBottom()) {\n                    child.resizeRelocate(\n                            snapPositionX(insets.getLeft()),\n                            snapPositionY(y),\n                            snapSizeX(w - insets.getRight() - insets.getLeft()),\n                            snapSizeY(ph));\n\n                    y += ph + 1; // +1 = gap\n                    child.getProperties().put(\"hidden\", false);\n                    childrenAdded++;\n                } else {\n                    if (!conflictFound && i > 0) {\n                        conflictFound = true;\n                        children.get(i - 1).getProperties().put(\"hidden\", true);\n                    }\n\n                    child.getProperties().put(\"hidden\", true);\n                }\n            }\n\n            if (conflictFound) {\n                moreLabel.setVisible(true);\n                moreLabel.setText(MessageFormat.format(Messages.getString(\"MonthViewSkin.MORE_ENTRIES\"), (childrenAdded == 0) ? children.size()-1 : children.size() - childrenAdded));\n                double ph = moreLabel.prefHeight(-1);\n\n                moreLabel.resizeRelocate(\n                        snapPositionX(insets.getLeft()),\n                        snapPositionY(h - insets.getTop() - insets.getBottom() - ph),\n                        snapSizeX(w - insets.getRight() - insets.getLeft()),\n                        snapSizeY(ph));\n            }\n        }           \n    }\n\n    public ZonedDateTime getZonedDateTimeAt(double x, double y, ZoneId zoneId) {\n        Point2D location = getSkinnable().localToScreen(x, y);\n        for (MonthDayView view : controlsMap.values()) {\n            if (view.localToScreen(view.getLayoutBounds()).contains(location)) {\n                return ZonedDateTime.of(view.getDate(), LocalTime.NOON, zoneId);\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/NavigateDateView.java",
    "content": "package impl.com.calendarfx.view;\n\nimport com.calendarfx.view.ButtonBar;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.scene.control.Button;\n\npublic class NavigateDateView extends ButtonBar {\n\n    private final Button backButton = new Button(\"<\");\n    private final Button todayButton = new Button(\"Today\");\n    private final Button forwardButton = new Button(\">\");\n\n    public NavigateDateView() {\n        super(FXCollections.observableArrayList());\n\n        backButton.getStyleClass().add(\"previous-date-button\");\n        todayButton.getStyleClass().add(\"today-button\");\n        forwardButton.getStyleClass().add(\"next-date-button\");\n\n        backButton.disableProperty().bind(onBackward.isNull());\n        todayButton.disableProperty().bind(onToday.isNull());\n        forwardButton.disableProperty().bind(onForward.isNull());\n\n        backButton.setOnAction(evt -> getOnBackward().run());\n        forwardButton.setOnAction(evt -> getOnForward().run());\n        todayButton.setOnAction(evt -> getOnToday().run());\n\n        getButtons().setAll(backButton, todayButton, forwardButton);\n        getStyleClass().add(\"navigate-date-view\");\n    }\n\n    public Button getBackButton() {\n        return backButton;\n    }\n\n    public Button getTodayButton() {\n        return todayButton;\n    }\n\n    public Button getForwardButton() {\n        return forwardButton;\n    }\n\n    private final ObjectProperty<Runnable> onForward = new SimpleObjectProperty<>(this, \"onForward\");\n\n    public Runnable getOnForward() {\n        return onForward.get();\n    }\n\n    public ObjectProperty<Runnable> onForwardProperty() {\n        return onForward;\n    }\n\n    public void setOnForward(Runnable onForward) {\n        this.onForward.set(onForward);\n    }\n\n    private final ObjectProperty<Runnable> onToday = new SimpleObjectProperty<>(this, \"onToday\");\n\n    public Runnable getOnToday() {\n        return onToday.get();\n    }\n\n    public ObjectProperty<Runnable> onTodayProperty() {\n        return onToday;\n    }\n\n    public void setOnToday(Runnable onToday) {\n        this.onToday.set(onToday);\n    }\n\n    private final ObjectProperty<Runnable> onBackward = new SimpleObjectProperty<>(this, \"onBackward\");\n\n    public Runnable getOnBackward() {\n        return onBackward.get();\n    }\n\n    public ObjectProperty<Runnable> onBackwardProperty() {\n        return onBackward;\n    }\n\n    public void setOnBackward(Runnable onBackward) {\n        this.onBackward.set(onBackward);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/NumericTextField.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport javafx.scene.control.TextField;\n\npublic final class NumericTextField extends TextField {\n\n    public NumericTextField() {\n        focusedProperty().addListener(it -> {\n            if (isFocused()) {\n                selectAll();\n            }\n        });\n    }\n\n    @Override\n    public void replaceText(int start, int end, String s) {\n        super.replaceText(start, end, s.replaceAll(\"[^0-9]\", \"\"));\n    }\n\n    @Override\n    public void replaceSelection(String s) {\n        super.replaceSelection(s.replaceAll(\"[^0-9]\", \"\"));\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/RecurrenceViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.RecurrenceView;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.HPos;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.util.StringConverter;\nimport net.fortuna.ical4j.model.Recur;\nimport net.fortuna.ical4j.model.WeekDay;\nimport net.fortuna.ical4j.model.WeekDay.Day;\nimport net.fortuna.ical4j.model.WeekDayList;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\nimport java.time.format.FormatStyle;\nimport java.util.List;\nimport java.util.logging.Level;\n\nimport static javafx.scene.layout.Region.USE_COMPUTED_SIZE;\nimport static net.fortuna.ical4j.transform.recurrence.Frequency.DAILY;\nimport static net.fortuna.ical4j.transform.recurrence.Frequency.MONTHLY;\nimport static net.fortuna.ical4j.transform.recurrence.Frequency.WEEKLY;\nimport static net.fortuna.ical4j.transform.recurrence.Frequency.YEARLY;\n\n@SuppressWarnings(\"javadoc\")\npublic class RecurrenceViewSkin extends SkinBase<RecurrenceView> {\n\n    private final ComboBox<Frequency> frequencyBox = new ComboBox<>();\n    private final Spinner<Integer> repeatCountSpinner = new Spinner<>();\n    private final Label repeatCountGranularity = new Label();\n    private final Label startOnDateLabel = new Label();\n    private final RadioButton endsNeverButton = new RadioButton(Messages.getString(\"RecurrenceViewSkin.NEVER\"));\n    private final RadioButton endsAfterButton = new RadioButton(Messages.getString(\"RecurrenceViewSkin.AFTER\"));\n    private final Spinner<Integer> endsAfterCounterSpinner = new Spinner<>();\n    private final Label endsAfterOccurencesLabel = new Label(Messages.getString(\"RecurrenceViewSkin.OCCURENCES\"));\n    private final RadioButton endsOnButton = new RadioButton(Messages.getString(\"RecurrenceViewSkin.ON\"));\n    private final DatePicker endsOnDatePicker = new DatePicker(LocalDate.now());\n    private Label summaryLabel = new Label();\n    private final RadioButton repeatByDayOfTheMonth = new RadioButton(Messages.getString(\"RecurrenceViewSkin.DAY_OF_MONTH\"));\n    private final RadioButton repeatByDayOfTheWeek = new RadioButton(Messages.getString(\"RecurrenceViewSkin.DAY_OF_WEEK\"));\n    private final ToggleButton weekDayMondayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_MONDAY\"));\n    private final ToggleButton weekDayTuesdayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_TUESDAY\"));\n    private final ToggleButton weekDayWednesdayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_WEDNESDAY\"));\n    private final ToggleButton weekDayThursdayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_THURSDAY\"));\n    private final ToggleButton weekDayFridayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_FRIDAY\"));\n    private final ToggleButton weekDaySaturdayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_SATURDAY\"));\n    private final ToggleButton weekDaySundayButton = new ToggleButton(Messages.getString(\"RecurrenceViewSkin.SHORT_SUNDAY\"));\n    private final HBox weekdayBox;\n    private final HBox repeatByBox;\n    private final HBox repeatCountBox;\n    private final Label labelRepeatOn;\n    private final Label summary = new Label();\n    private final Label labelRepeatBy;\n    private final Label frequencyLabel;\n    private final Label repeatCountLabel;\n    private final Label startsOnLabel;\n    private final Label endsOnLabel;\n    private final HBox endsAfterBox;\n    private final HBox endsOnBox;\n    private final GridPane grid;\n    private final IntegerSpinnerValueFactory repeatCountSpinnerValueFactory;\n    private final IntegerSpinnerValueFactory endsAfterCounterSpinnerValueFactory;\n\n    enum Frequency {\n        DAILY,\n        WEEKLY,\n        MONTHLY,\n        YEARLY,\n    }\n\n    public RecurrenceViewSkin(RecurrenceView view) {\n        super(view);\n\n        /*\n         * Inits\n         */\n\n        summary.setMaxWidth(250);\n        summary.setWrapText(true);\n\n        frequencyBox.getItems().setAll(Frequency.values());\n        frequencyBox.setValue(Frequency.DAILY);\n        frequencyBox.setConverter(new StringConverter<Frequency>() {\n\n            @Override\n            public String toString(Frequency frequency) {\n                switch (frequency) {\n                    case DAILY:\n                        return Messages.getString(\"RecurrenceViewSkin.DAILY\");\n                    case MONTHLY:\n                        return Messages.getString(\"RecurrenceViewSkin.MONTHLY\");\n                    case WEEKLY:\n                        return Messages.getString(\"RecurrenceViewSkin.WEEKLY\");\n                    case YEARLY:\n                        return Messages.getString(\"RecurrenceViewSkin.YEARLY\");\n                    default:\n                        return \"\";\n                }\n            }\n\n            @Override\n            public Frequency fromString(String txt) {\n                return null;\n            }\n        });\n\n        repeatCountSpinnerValueFactory = new IntegerSpinnerValueFactory(1, 100);\n\n        repeatCountSpinner.setValueFactory(repeatCountSpinnerValueFactory);\n        repeatCountSpinner.setEditable(true);\n        repeatCountSpinner.getEditor().setPrefColumnCount(5);\n\n        endsAfterCounterSpinnerValueFactory = new IntegerSpinnerValueFactory(1, 1000);\n        endsAfterCounterSpinner.setValueFactory(endsAfterCounterSpinnerValueFactory);\n        endsAfterCounterSpinner.setEditable(true);\n        endsAfterCounterSpinner.getEditor().setPrefColumnCount(5);\n\n        endsAfterCounterSpinner.disableProperty().bind(Bindings.not(endsAfterButton.selectedProperty()));\n        endsAfterOccurencesLabel.disableProperty().bind(Bindings.not(endsAfterButton.selectedProperty()));\n        endsOnDatePicker.disableProperty().bind(Bindings.not(endsOnButton.selectedProperty()));\n\n        endsOnDatePicker.getEditor().setPrefColumnCount(14);\n\n        /*\n         * Button groups.\n         */\n        ToggleGroup endsByGroup = new ToggleGroup();\n        endsByGroup.getToggles().setAll(endsAfterButton, endsNeverButton, endsOnButton);\n\n        ToggleGroup repeatByGroup = new ToggleGroup();\n        repeatByGroup.getToggles().setAll(repeatByDayOfTheMonth, repeatByDayOfTheWeek);\n\n        ToggleGroup endsGroup = new ToggleGroup();\n        endsGroup.getToggles().setAll(endsOnButton, endsAfterButton, endsNeverButton);\n\n        /*\n         * Repeat count box.\n         */\n        repeatCountBox = new HBox();\n        repeatCountBox.getStyleClass().add(\"repeat-count-box\");\n        repeatCountBox.getChildren().setAll(repeatCountSpinner, repeatCountGranularity);\n\n        /*\n         * Weekday box.\n         */\n        weekdayBox = new HBox();\n        weekdayBox.getStyleClass().add(\"weekday-box\");\n        weekdayBox.getChildren().setAll(weekDayMondayButton,\n                weekDayTuesdayButton, weekDayWednesdayButton,\n                weekDayThursdayButton, weekDayFridayButton,\n                weekDaySaturdayButton, weekDaySundayButton);\n\n        /*\n         * Repeat by box.\n         */\n        repeatByBox = new HBox();\n        repeatByBox.getStyleClass().add(\"repeat-by-box\");\n        repeatByBox.getChildren().setAll(repeatByDayOfTheMonth, repeatByDayOfTheWeek);\n\n        /*\n         * Ends after box.\n         */\n        endsAfterBox = new HBox();\n        endsAfterBox.getStyleClass().add(\"ends-after-box\");\n        endsAfterBox.getChildren().setAll(endsAfterButton, endsAfterCounterSpinner, endsAfterOccurencesLabel);\n\n        /*\n         * Ends on box.\n         */\n        endsOnBox = new HBox();\n        endsOnBox.getStyleClass().add(\"ends-on-box\");\n        endsOnBox.getChildren().setAll(endsOnButton, endsOnDatePicker);\n\n        grid = new GridPane();\n        grid.getStyleClass().add(\"container\");\n\n        /*\n         * Columns\n         */\n        ColumnConstraints labelsColumn = new ColumnConstraints();\n        ColumnConstraints fieldsColumn = new ColumnConstraints();\n\n        labelsColumn.setHalignment(HPos.RIGHT);\n        labelsColumn.setPrefWidth(USE_COMPUTED_SIZE);\n\n        grid.getColumnConstraints().setAll(labelsColumn, fieldsColumn);\n\n        frequencyLabel = new Label(Messages.getString(\"RecurrenceViewSkin.FREQUENCY\"));\n        repeatCountLabel = new Label(Messages.getString(\"RecurrenceViewSkin.REPEAT_EVERY\"));\n        labelRepeatOn = new Label(Messages.getString(\"RecurrenceViewSkin.REPEAT_ON\"));\n        labelRepeatBy = new Label(Messages.getString(\"RecurrenceViewSkin.REPEAT_BY\"));\n        startsOnLabel = new Label(Messages.getString(\"RecurrenceViewSkin.STARTS_ON\"));\n        endsOnLabel = new Label(Messages.getString(\"RecurrenceViewSkin.ENDS\"));\n        summaryLabel = new Label(Messages.getString(\"RecurrenceViewSkin.SUMMARY\"));\n\n        grid.add(frequencyLabel, 0, 0);\n        grid.add(frequencyBox, 1, 0);\n        grid.add(repeatCountLabel, 0, 1);\n        grid.add(repeatCountBox, 1, 1);\n\n        buildGrid();\n\n        frequencyBox.setMaxWidth(Double.MAX_VALUE);\n        GridPane.setFillWidth(frequencyBox, true);\n\n        getChildren().add(grid);\n\n        InvalidationListener updateListener = it -> updateRule();\n\n        frequencyBox.valueProperty().addListener(updateListener);\n        frequencyBox.valueProperty().addListener(it -> buildGrid());\n        repeatCountSpinner.valueProperty().addListener(updateListener);\n\n        weekDayMondayButton.selectedProperty().addListener(updateListener);\n        weekDayTuesdayButton.selectedProperty().addListener(updateListener);\n        weekDayWednesdayButton.selectedProperty().addListener(updateListener);\n        weekDayThursdayButton.selectedProperty().addListener(updateListener);\n        weekDayFridayButton.selectedProperty().addListener(updateListener);\n        weekDaySaturdayButton.selectedProperty().addListener(updateListener);\n        weekDaySundayButton.selectedProperty().addListener(updateListener);\n        repeatByDayOfTheMonth.selectedProperty().addListener(updateListener);\n        repeatByDayOfTheWeek.selectedProperty().addListener(updateListener);\n        endsNeverButton.selectedProperty().addListener(updateListener);\n        endsAfterButton.selectedProperty().addListener(updateListener);\n        endsAfterCounterSpinner.valueProperty().addListener(updateListener);\n        endsOnButton.selectedProperty().addListener(updateListener);\n        endsOnDatePicker.valueProperty().addListener(updateListener);\n\n        getSkinnable().showSummaryProperty().addListener(it -> buildGrid());\n\n        view.recurrenceRuleProperty().addListener(it -> updateView());\n\n        updateView();\n    }\n\n    private void buildGrid() {\n        grid.getChildren().removeIf(node -> !(node == frequencyLabel || node == frequencyBox\n                || node == repeatCountLabel || node == repeatCountBox));\n\n        int row = 2;\n\n        if (frequencyBox.getValue().equals(Frequency.WEEKLY)) {\n            grid.add(labelRepeatOn, 0, row);\n            grid.add(weekdayBox, 1, row);\n            row++;\n        }\n\n        if (frequencyBox.getValue().equals(Frequency.MONTHLY)) {\n            grid.add(labelRepeatBy, 0, row);\n            grid.add(repeatByBox, 1, row);\n            row++;\n        }\n\n        grid.add(startsOnLabel, 0, row);\n        grid.add(startOnDateLabel, 1, row);\n        row++;\n\n        grid.add(endsOnLabel, 0, row);\n        grid.add(endsNeverButton, 1, row);\n        row++;\n\n        grid.add(endsAfterBox, 1, row);\n        row++;\n\n        grid.add(endsOnBox, 1, row);\n        row++;\n\n        if (getSkinnable().isShowSummary()) {\n            grid.add(summaryLabel, 0, row);\n            grid.add(summary, 1, row);\n        }\n    }\n\n    private void updateView() {\n        try {\n            String rule = getSkinnable().getRecurrenceRule();\n            if (rule == null) {\n                return;\n            }\n            Recur<LocalDate> rrule = new Recur<>(rule.replaceFirst(\"^RRULE:\", \"\"));\n            switch (rrule.getFrequency()) {\n                case DAILY:\n                    frequencyBox.setValue(Frequency.DAILY);\n                    repeatCountGranularity.setText(Messages.getString(\"RecurrenceViewSkin.DAYS\"));\n                    break;\n                case WEEKLY:\n                    frequencyBox.setValue(Frequency.WEEKLY);\n                    repeatCountGranularity.setText(Messages.getString(\"RecurrenceViewSkin.32\"));\n                    break;\n                case MONTHLY:\n                    frequencyBox.setValue(Frequency.MONTHLY);\n                    repeatCountGranularity.setText(Messages.getString(\"RecurrenceViewSkin.MONTHS\"));\n                    break;\n                case YEARLY:\n                    frequencyBox.setValue(Frequency.YEARLY);\n                    repeatCountGranularity.setText(Messages.getString(\"RecurrenceViewSkin.YEARS\"));\n                    break;\n                case SECONDLY:\n                case HOURLY:\n                case MINUTELY:\n                    throw new IllegalArgumentException(\n                            \"unsupported frequency: \" + rrule.getFrequency());\n                default:\n                    throw new IllegalArgumentException(\n                            \"unknown frequency: \" + rrule.getFrequency());\n\n            }\n\n            LocalDate until = rrule.getUntil();\n            if (until != null) {\n                endsOnButton.setSelected(true);\n                endsOnDatePicker.setValue(until);\n            } else if (rrule.getCount() > 0) {\n                endsAfterButton.setSelected(true);\n            } else {\n                endsNeverButton.setSelected(true);\n            }\n\n            if (!rrule.getMonthDayList().isEmpty()) {\n                repeatByDayOfTheMonth.setSelected(true);\n            } else {\n                repeatByDayOfTheWeek.setSelected(true);\n            }\n\n            repeatCountSpinnerValueFactory.setValue(rrule.getInterval());\n            endsAfterCounterSpinnerValueFactory.setValue(rrule.getCount());\n\n            List<WeekDay> days = rrule.getDayList();\n\n            weekDayMondayButton.setSelected(isSelected(Day.MO, days));\n            weekDayTuesdayButton.setSelected(isSelected(Day.TU, days));\n            weekDayWednesdayButton.setSelected(isSelected(Day.WE, days));\n            weekDayThursdayButton.setSelected(isSelected(Day.TH, days));\n            weekDayFridayButton.setSelected(isSelected(Day.FR, days));\n            weekDaySaturdayButton.setSelected(isSelected(Day.SA, days));\n            weekDaySundayButton.setSelected(isSelected(Day.SU, days));\n\n            summary.setText(Util.convertRFC2445ToText(rule, getSkinnable().getStartDate()));\n        } catch (IllegalArgumentException | DateTimeParseException e) {\n            e.printStackTrace();\n        }\n\n        startOnDateLabel.setText(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(getSkinnable().getStartDate()));\n    }\n\n    private boolean isSelected(WeekDay.Day day, List<WeekDay> days) {\n        for (WeekDay num : days) {\n            if (num.getDay().equals(day)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    private void updateRule() {\n        Recur.Builder<LocalDate> rBuilder = new Recur.Builder<>();\n        switch (frequencyBox.getValue()) {\n            case DAILY:\n                rBuilder.frequency(DAILY);\n                break;\n            case MONTHLY:\n                rBuilder.frequency(MONTHLY);\n                break;\n            case WEEKLY:\n                rBuilder.frequency(WEEKLY);\n                break;\n            case YEARLY:\n                rBuilder.frequency(YEARLY);\n                break;\n            default:\n                break;\n        }\n\n        int interval = repeatCountSpinner.getValue();\n        if (interval > 1) {\n            rBuilder.interval(interval);\n        } else {\n            rBuilder.interval(0);\n        }\n\n        if (endsOnButton.isSelected()) {\n            LocalDate date = endsOnDatePicker.getValue();\n            rBuilder.until(date);\n        }\n\n        if (endsAfterButton.isSelected()) {\n            rBuilder.count(endsAfterCounterSpinner.getValue());\n        }\n\n        RecurrenceView recurrenceView = getSkinnable();\n\n        if (frequencyBox.getValue() == Frequency.MONTHLY) {\n            if (repeatByDayOfTheMonth.isSelected()) {\n                rBuilder.monthDayList(List.of(recurrenceView.getStartDate().getDayOfMonth()));\n            } else {\n                LocalDate localDate = recurrenceView.getStartDate();\n\n                ZonedDateTime zonedDateTime = ZonedDateTime.of(localDate, LocalTime.now(), ZoneId.systemDefault());\n                int hits = 1;\n                ZonedDateTime current = zonedDateTime.withDayOfMonth(1);\n                do {\n                    if (current.getDayOfWeek().equals(zonedDateTime.getDayOfWeek())) {\n                        hits++;\n                    }\n                    current = current.plusDays(1);\n                } while (current.toLocalDate().isBefore(localDate));\n\n                WeekDayList weekdays = new WeekDayList();\n                switch (zonedDateTime.getDayOfWeek()) {\n                    case FRIDAY:\n                        weekdays.add(new WeekDay(WeekDay.FR, hits));\n                        break;\n                    case MONDAY:\n                        weekdays.add(new WeekDay(WeekDay.MO, hits));\n                        break;\n                    case SATURDAY:\n                        weekdays.add(new WeekDay(WeekDay.SA, hits));\n                        break;\n                    case SUNDAY:\n                        weekdays.add(new WeekDay(WeekDay.SU, hits));\n                        break;\n                    case THURSDAY:\n                        weekdays.add(new WeekDay(WeekDay.TH, hits));\n                        break;\n                    case TUESDAY:\n                        weekdays.add(new WeekDay(WeekDay.TU, hits));\n                        break;\n                    case WEDNESDAY:\n                        weekdays.add(new WeekDay(WeekDay.WE, hits));\n                        break;\n                    default:\n                        break;\n                }\n\n                rBuilder.dayList(weekdays);\n            }\n        }\n\n        if (frequencyBox.getValue() == Frequency.WEEKLY) {\n\n            /*\n             * Weekdays MO, TU, .... SU\n             */\n            WeekDayList weekdays = new WeekDayList();\n\n            maybeAddWeekday(weekdays, WeekDay.MO,\n                    weekDayMondayButton);\n            maybeAddWeekday(weekdays, WeekDay.TU,\n                    weekDayTuesdayButton);\n            maybeAddWeekday(weekdays, WeekDay.WE,\n                    weekDayWednesdayButton);\n            maybeAddWeekday(weekdays, WeekDay.TH,\n                    weekDayThursdayButton);\n            maybeAddWeekday(weekdays, WeekDay.FR,\n                    weekDayFridayButton);\n            maybeAddWeekday(weekdays, WeekDay.SA,\n                    weekDaySaturdayButton);\n            maybeAddWeekday(weekdays, WeekDay.SU,\n                    weekDaySundayButton);\n\n            rBuilder.dayList(weekdays);\n        }\n\n        Recur<LocalDate> rule = rBuilder.build();\n        recurrenceView.setRecurrenceRule(rule.toString());\n\n        if (LoggingDomain.RECURRENCE.isLoggable(Level.FINE)) {\n            LoggingDomain.RECURRENCE.fine(\n                    \"test dumping 10 recurrences starting with today's date\");\n\n            LocalDate today = LocalDate.of(2015, 8, 18);\n            List<LocalDate> dates = rule.getDates(today, today, LocalDate.MAX, 10);\n\n            for (LocalDate repeatingDate : dates) {\n                LoggingDomain.RECURRENCE.fine(repeatingDate.toString());\n            }\n        }\n    }\n\n    private void maybeAddWeekday(WeekDayList weekdays,\n                                 WeekDay weekdayNum, ToggleButton button) {\n        if (button.isSelected()) {\n            weekdays.add(weekdayNum);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ResourceCalendarViewSkin.java",
    "content": "package impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Marker;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.ResourceCalendarView;\nimport com.calendarfx.view.TimeScaleView;\nimport com.calendarfx.view.VirtualGrid;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.ObservableMap;\nimport javafx.geometry.HPos;\nimport javafx.geometry.Orientation;\nimport javafx.scene.Cursor;\nimport javafx.scene.Node;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.shape.Rectangle;\nimport org.controlsfx.control.PlusMinusSlider;\n\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\n\npublic class ResourceCalendarViewSkin<T> extends DayViewBaseSkin<ResourceCalendarView<T>> {\n\n    private final GridPane gridPane = new GridPane();\n\n    private final HeaderGridPane headerGridPane = new HeaderGridPane();\n\n    private final BodyGridPane bodyGridPane = new BodyGridPane();\n\n    private final TimeScaleView timeScaleView = new TimeScaleView();\n\n    private PlusMinusSlider slider;\n\n    public ResourceCalendarViewSkin(ResourceCalendarView view) {\n        super(view);\n\n        timeScaleView.setScrollingEnabled(true);\n        timeScaleView.visibleProperty().bind(view.showTimeScaleProperty());\n        timeScaleView.managedProperty().bind(view.showTimeScaleProperty());\n        view.bind(timeScaleView, true);\n\n        gridPane.getStyleClass().add(\"resource-calendar-container\");\n\n        getChildren().add(gridPane);\n\n        RowConstraints row1 = new RowConstraints();\n        RowConstraints row2 = new RowConstraints();\n\n        row1.setVgrow(Priority.NEVER);\n        row2.setVgrow(Priority.ALWAYS);\n\n        gridPane.getRowConstraints().setAll(row1, row2);\n\n        final InvalidationListener updateGridPaneListener = it -> updateView();\n\n        view.dayViewMapProperty().addListener(updateGridPaneListener);\n        view.showScrollBarProperty().addListener(updateGridPaneListener);\n        view.getMarkers().addListener(updateGridPaneListener);\n\n        view.showScrollBarProperty().addListener(it -> updateColumnConstraints());\n\n        updateColumnConstraints();\n        updateView();\n    }\n\n    private void updateColumnConstraints() {\n        ColumnConstraints con1 = new ColumnConstraints();\n        con1.setPrefWidth(Region.USE_COMPUTED_SIZE);\n        con1.setFillWidth(true);\n\n        ColumnConstraints con2 = new ColumnConstraints();\n        con2.setFillWidth(true);\n        con2.setHgrow(Priority.ALWAYS);\n\n        gridPane.getColumnConstraints().setAll(con1, con2);\n\n        if (getSkinnable().isShowScrollBar()) {\n            ColumnConstraints con3 = new ColumnConstraints();\n            con3.setPrefWidth(Region.USE_COMPUTED_SIZE);\n            con3.setFillWidth(true);\n            gridPane.getColumnConstraints().add(con3);\n        }\n    }\n\n    private void updateView() {\n        gridPane.getChildren().clear();\n        gridPane.add(timeScaleView, 0, 1);\n\n        Region header = new Region();\n        header.getStyleClass().add(\"header-background\");\n        gridPane.add(header, 0, 0);\n        GridPane.setColumnSpan(header, 3);\n\n        // header\n        headerGridPane.updateView();\n        gridPane.add(headerGridPane, 1, 0);\n        GridPane.setFillWidth(headerGridPane, true);\n\n        // day views\n        bodyGridPane.updateView();\n        gridPane.add(bodyGridPane, 1, 1);\n        GridPane.setFillWidth(bodyGridPane, true);\n\n        ResourceCalendarView<T> resourceCalendarView = getSkinnable();\n        if (resourceCalendarView.isShowScrollBar()) {\n            slider = new PlusMinusSlider();\n            slider.setOrientation(Orientation.VERTICAL);\n            slider.setOnValueChanged(evt -> {\n                // exponential function to increase scrolling speed when reaching ends of slider\n                final double base = slider.getValue();\n                final double pow = Math.signum(slider.getValue()) * Math.pow(base, 2);\n                final double pixel = pow * -100;\n                resourceCalendarView.setScrollTime(resourceCalendarView.getZonedDateTimeAt(0, pixel, resourceCalendarView.getZoneId()));\n            });\n\n            gridPane.add(slider, 2, 1);\n        }\n    }\n\n    public class HeaderGridPane extends GridPane {\n\n        public HeaderGridPane() {\n        }\n\n        private void updateView() {\n            getChildren().clear();\n            getColumnConstraints().clear();\n\n            final int columnCounts = getSkinnable().getResources().size();\n\n            for (int i = 0; i < columnCounts; i++) {\n                ColumnConstraints con = new ColumnConstraints();\n                con.setHalignment(HPos.CENTER);\n                con.setFillWidth(true);\n                con.setHgrow(Priority.ALWAYS);\n                con.setPercentWidth(100d / columnCounts);\n\n                getColumnConstraints().add(con);\n\n                T resource = getSkinnable().getResources().get(i);\n\n                Node resourceHeader = getSkinnable().getHeaderFactory().call(resource);\n\n                GridPane.setFillHeight(resourceHeader, true);\n                GridPane.setVgrow(resourceHeader, Priority.ALWAYS);\n\n                add(resourceHeader, i, 1);\n                GridPane.setRowSpan(resourceHeader, 1);\n            }\n        }\n    }\n\n    public class BodyGridPane extends GridPane {\n\n        private MarkerLine draggedMarkerLine;\n\n        private final InvalidationListener markerListener = it -> Platform.runLater(() -> getSkinnable().layout());\n\n        private final WeakInvalidationListener weakMarkerListener = new WeakInvalidationListener(markerListener);\n\n        private final ObservableMap<Marker, MarkerLine> markerLineMap = FXCollections.observableMap(new HashMap<>());\n\n        private double startY;\n\n        public BodyGridPane() {\n\n            addEventFilter(MouseEvent.MOUSE_MOVED, evt -> {\n                if (evt.getTarget() instanceof MarkerLine) {\n                    final MarkerLine markerLine = (MarkerLine) evt.getTarget();\n                    if (markerLine.getMarker().isMovable()) {\n                        markerLine.setCursor(Cursor.HAND);\n                    }\n                }\n            });\n\n            addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> {\n                startY = evt.getScreenY();\n                if (evt.getTarget() instanceof MarkerLine) {\n                    final MarkerLine markerLine = (MarkerLine) evt.getTarget();\n                    if (markerLine.getMarker().isMovable()) {\n                        draggedMarkerLine = (MarkerLine) evt.getTarget();\n                        draggedMarkerLine.setCursor(Cursor.CLOSED_HAND);\n                    }\n                }\n            });\n\n            addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {\n                if (draggedMarkerLine != null) {\n                    draggedMarkerLine.setCursor(Cursor.HAND);\n                    final double y = draggedMarkerLine.getLayoutY();\n                    adjustLineLocation(draggedMarkerLine, y);\n                    draggedMarkerLine = null;\n                }\n            });\n\n            addEventFilter(MouseEvent.MOUSE_DRAGGED, evt -> {\n                if (draggedMarkerLine != null) {\n                    double y = evt.getScreenY();\n                    double delta = startY - y;\n                    double newLocation = draggedMarkerLine.getLayoutY() - delta;\n                    draggedMarkerLine.setLayoutY(newLocation);\n                    startY = y;\n                }\n            });\n\n            ListChangeListener<Marker> l = change -> {\n                while (change.next()) {\n                    if (change.wasAdded()) {\n                        change.getAddedSubList().forEach(marker -> addMarkerLine(marker));\n                    } else if (change.wasRemoved()) {\n                        change.getRemoved().forEach(marker -> {\n                            marker.timeProperty().removeListener(weakMarkerListener);\n                            getChildren().remove(markerLineMap.get(marker));\n                        });\n                    }\n                }\n            };\n\n            getSkinnable().getMarkers().addListener(l);\n\n            final ObservableList<Marker> markers = getSkinnable().getMarkers();\n            markers.forEach(marker -> addMarkerLine(marker));\n\n            Rectangle clip = new Rectangle();\n            clip.widthProperty().bind(widthProperty());\n            clip.heightProperty().bind(heightProperty());\n            setClip(clip);\n        }\n\n        private void adjustLineLocation(MarkerLine markerLine, double y) {\n            ZonedDateTime dropTime = getSkinnable().getZonedDateTimeAt(0, y, getSkinnable().getZoneId());\n            final VirtualGrid virtualGrid = getSkinnable().getVirtualGrid();\n            if (virtualGrid != null) {\n\n                ZonedDateTime timeA = virtualGrid.adjustTime(dropTime, true, getSkinnable().getFirstDayOfWeek());\n                ZonedDateTime timeB = virtualGrid.adjustTime(dropTime, false, getSkinnable().getFirstDayOfWeek());\n\n                final long secondsA = Math.abs(timeA.until(dropTime, ChronoUnit.SECONDS));\n                final long secondsB = Math.abs(timeB.until(dropTime, ChronoUnit.SECONDS));\n\n                // \"jump / snap\" to the closes grid time\n                if (secondsA < secondsB) {\n                    dropTime = timeA;\n                } else {\n                    dropTime = timeB;\n                }\n            }\n\n            markerLine.getMarker().setTime(dropTime);\n            final double dropLocationY = getSkinnable().getLocation(dropTime);\n            markerLine.setLayoutY(dropLocationY - markerLine.prefHeight(-1));\n        }\n\n        private void addMarkerLine(Marker marker) {\n            marker.timeProperty().addListener(weakMarkerListener);\n            MarkerLine markerLine = new MarkerLine(marker);\n            markerLineMap.put(marker, markerLine);\n            markerLine.setManaged(false);\n            getChildren().add(markerLine);\n        }\n\n        @Override\n        protected void layoutChildren() {\n            super.layoutChildren();\n\n            markerLineMap.values().forEach(line -> {\n                final Marker marker = line.getMarker();\n\n                /*\n                 * Very important not to lay out the currently dragged marker line.\n                 */\n                if (line != draggedMarkerLine) {\n                    final ZonedDateTime time = marker.getTime();\n                    final double location = getSkinnable().getLocation(time);\n                    MarkerLine markerLine = markerLineMap.get(marker);\n                    double ph = markerLine.prefHeight(-1);\n\n                    markerLine.toFront();\n\n                    double x = getInsets().getLeft();\n                    double w = getWidth() - getInsets().getLeft() - getInsets().getRight();\n\n                    markerLine.resizeRelocate(x, snapPositionY(location - ph / 2), snapSizeX(w), snapSizeY(ph));\n                }\n            });\n        }\n\n        private void updateView() {\n            getChildren().removeIf(node -> !(node instanceof MarkerLine));\n            getColumnConstraints().clear();\n\n            final int columnCounts = getSkinnable().getResources().size();\n\n            for (int i = 0; i < columnCounts; i++) {\n                T resource = getSkinnable().getResources().get(i);\n\n                ColumnConstraints con = new ColumnConstraints();\n                con.setHalignment(HPos.CENTER);\n                con.setFillWidth(true);\n                con.setHgrow(Priority.ALWAYS);\n                con.setPercentWidth(100d / columnCounts);\n\n                getColumnConstraints().add(con);\n\n                DayView dayView = getSkinnable().getDayView(resource);\n\n                GridPane.setFillHeight(dayView, true);\n                GridPane.setVgrow(dayView, Priority.ALWAYS);\n\n                add(dayView, i, 1);\n                GridPane.setRowSpan(dayView, 1);\n            }\n        }\n    }\n\n    private static class MarkerLine extends StackPane {\n\n        private final Marker marker;\n\n        private final InvalidationListener updateStyleListener = (Observable it) -> updateStyleClass();\n\n        private final WeakInvalidationListener weakUpdateStyleListener = new WeakInvalidationListener(updateStyleListener);\n\n        public MarkerLine(Marker marker) {\n            this.marker = marker;\n\n            styleProperty().bind(marker.styleProperty());\n            marker.getStyleClass().addListener(weakUpdateStyleListener);\n            updateStyleClass();\n\n            Tooltip tooltip = new Tooltip();\n            tooltip.textProperty().bind(marker.titleProperty());\n            Tooltip.install(this, tooltip);\n        }\n\n        private void updateStyleClass() {\n            getStyleClass().setAll(\"marker-line\");\n            getStyleClass().addAll(marker.getStyleClass());\n        }\n\n        public Marker getMarker() {\n            return marker;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ResourcesViewContainer.java",
    "content": "package impl.com.calendarfx.view;\n\nimport com.calendarfx.view.DayViewBase;\nimport com.calendarfx.model.Resource;\nimport com.calendarfx.view.ResourcesView;\nimport javafx.scene.control.Skin;\n\npublic class ResourcesViewContainer<T extends Resource<?>> extends DayViewBase {\n\n    private final ResourcesView<T> resourcesView;\n\n    public ResourcesViewContainer(ResourcesView<T> view) {\n        this.resourcesView = view;\n        getStyleClass().add(\"resources-view-container\");\n        setShowToday(false);\n    }\n\n    @Override\n    protected Skin<?> createDefaultSkin() {\n        return new ResourcesViewContainerSkin<>(this);\n    }\n\n    public final ResourcesView<T> getResourcesView() {\n        return resourcesView;\n    }\n}\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ResourcesViewContainerSkin.java",
    "content": "package impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Resource;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.ResourcesView;\nimport com.calendarfx.view.ResourcesView.Type;\nimport com.calendarfx.view.WeekView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.ObservableList;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\n\npublic class ResourcesViewContainerSkin<T extends Resource<?>> extends DayViewBaseSkin<ResourcesViewContainer<T>> {\n\n    private final HBox box = new HBox();\n\n    private final ResourcesView<T> resourcesView;\n\n    public ResourcesViewContainerSkin(ResourcesViewContainer<T> view) {\n        super(view);\n\n        box.getStyleClass().add(\"container\");\n        box.setFillHeight(true);\n\n        resourcesView = view.getResourcesView();\n\n        InvalidationListener updateViewListener = (Observable it) -> updateView();\n        resourcesView.getResources().addListener(updateViewListener);\n        resourcesView.numberOfDaysProperty().addListener(updateViewListener);\n        resourcesView.typeProperty().addListener(updateViewListener);\n\n        updateView();\n\n        getChildren().add(box);\n    }\n\n    private void updateView() {\n        box.getChildren().clear();\n\n        ResourcesViewContainer<T> resourcesContainer = getSkinnable();\n        resourcesContainer.unbindAll();\n\n        if (resourcesView.getType().equals(Type.RESOURCES_OVER_DATES)) {\n            updateViewResourcesOverDates();\n        } else {\n            updateViewDatesOverResources();\n        }\n    }\n\n    private void updateViewDatesOverResources() {\n        ResourcesViewContainer<T> resourcesViewContainer = getSkinnable();\n        ObservableList<T> resources = resourcesView.getResources();\n        int numberOfDays = resourcesView.getNumberOfDays();\n\n        for (int dayIndex = 0; dayIndex < numberOfDays; dayIndex++) {\n\n            HBox resourcesBox = new HBox();\n\n            for (int resourceIndex = 0; resourceIndex < resources.size(); resourceIndex++) {\n                T resource = resources.get(resourceIndex);\n\n                DayView dayView = resourcesView.getDayViewFactory().call(resource);\n\n                final int additionalDays = dayIndex;\n\n                dayView.dateProperty().bind(Bindings.createObjectBinding(() -> resourcesViewContainer.getDate().plusDays(additionalDays), resourcesViewContainer.dateProperty()));\n\n                dayView.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n                if (resources.size() == 1) {\n                    dayView.getStyleClass().add(\"only\");\n                } else {\n                    if (resourceIndex == 0) {\n                        dayView.getStyleClass().add(\"first\");\n                    } else if (resourceIndex == resources.size() - 1) {\n                        dayView.getStyleClass().add(\"last\");\n                    } else {\n                        dayView.getStyleClass().add(\"middle\");\n                    }\n                }\n\n                // bind day view to container but remove bindings that interfere\n                resourcesViewContainer.bind(dayView, false);\n\n                // unbind what is not needed\n                Bindings.unbindBidirectional(resourcesViewContainer.defaultCalendarProviderProperty(), dayView.defaultCalendarProviderProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.draggedEntryProperty(), dayView.draggedEntryProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.enableCurrentTimeMarkerProperty(), dayView.enableCurrentTimeMarkerProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.enableCurrentTimeCircleProperty(), dayView.enableCurrentTimeCircleProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.availabilityCalendarProperty(), dayView.availabilityCalendarProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.lassoStartProperty(), dayView.lassoStartProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.lassoEndProperty(), dayView.lassoEndProperty());\n                Bindings.unbindBidirectional(resourcesViewContainer.onLassoFinishedProperty(), dayView.onLassoFinishedProperty());\n                Bindings.unbindContentBidirectional(resourcesViewContainer.getCalendarSources(), dayView.getCalendarSources());\n\n                dayView.setEnableCurrentTimeMarker(true);\n                dayView.setEnableCurrentTimeCircle(dayIndex == 0 && resourceIndex == 0);\n\n                dayView.setAvailabilityCalendar(resource.getAvailabilityCalendar());\n                dayView.installDefaultLassoFinishedBehaviour();\n\n                Bindings.bindContent(dayView.getCalendarSources(), resource.getCalendarSources());\n                dayView.setDefaultCalendarProvider(dateControl -> resource.getCalendars().get(0));\n\n                dayView.setPrefWidth(0); // so they all end up with the same percentage width\n                dayView.setMinHeight(0);\n                HBox.setHgrow(dayView, Priority.ALWAYS);\n                resourcesBox.getChildren().add(dayView);\n\n                if (resourceIndex < resources.size() - 1) {\n                    Region separator = new Region();\n                    separator.getStyleClass().add(\"weekday-separator\"); // not really separating weekdays, but we want it to look the same\n                    resourcesBox.getChildren().add(separator);\n                    HBox.setHgrow(separator, Priority.NEVER);\n                }\n            }\n\n            this.box.getChildren().add(resourcesBox);\n\n            if (dayIndex < numberOfDays - 1) {\n                Callback<ResourcesView<T>, Region> separatorFactory = resourcesView.getLargeSeparatorFactory();\n                if (separatorFactory != null) {\n                    Region separator = separatorFactory.call(resourcesViewContainer.getResourcesView());\n                    if (separator != null) {\n                        this.box.getChildren().add(separator);\n                        HBox.setHgrow(separator, Priority.NEVER);\n                    }\n                }\n            }\n            HBox.setHgrow(resourcesBox, Priority.ALWAYS);\n        }\n    }\n\n    private void updateViewResourcesOverDates() {\n        ResourcesViewContainer<T> container = getSkinnable();\n        ObservableList<T> resources = resourcesView.getResources();\n        for (int i = 0; i < resources.size(); i++) {\n            T resource = resources.get(i);\n\n            WeekView weekView = resourcesView.getWeekViewFactory().call(resource);\n\n            weekView.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n            if (resources.size() == 1) {\n                weekView.getStyleClass().add(\"only\");\n            } else {\n                if (i == 0) {\n                    weekView.getStyleClass().add(\"first\");\n                } else if (i == resources.size() - 1) {\n                    weekView.getStyleClass().add(\"last\");\n                } else {\n                    weekView.getStyleClass().add(\"middle\");\n                }\n            }\n\n            weekView.setPrefWidth(0); // so they all end up with the same percentage width\n\n            // bind day view to container but remove bindings that interfere\n            container.bind(weekView, true);\n\n            // bind additionally \"adjust\"\n            weekView.adjustToFirstDayOfWeekProperty().bind(resourcesView.adjustToFirstDayOfWeekProperty());\n\n            // unbind what is not needed\n            Bindings.unbindBidirectional(container.defaultCalendarProviderProperty(), weekView.defaultCalendarProviderProperty());\n            Bindings.unbindBidirectional(container.draggedEntryProperty(), weekView.draggedEntryProperty());\n            Bindings.unbindBidirectional(container.enableCurrentTimeMarkerProperty(), weekView.enableCurrentTimeMarkerProperty());\n            Bindings.unbindBidirectional(container.enableCurrentTimeCircleProperty(), weekView.enableCurrentTimeCircleProperty());\n            Bindings.unbindBidirectional(container.availabilityCalendarProperty(), weekView.availabilityCalendarProperty());\n            Bindings.unbindBidirectional(container.lassoStartProperty(), weekView.lassoStartProperty());\n            Bindings.unbindBidirectional(container.lassoEndProperty(), weekView.lassoEndProperty());\n            Bindings.unbindBidirectional(container.onLassoFinishedProperty(), weekView.onLassoFinishedProperty());\n\n            Bindings.unbindContentBidirectional(container.getCalendarSources(), weekView.getCalendarSources());\n\n            weekView.setEnableCurrentTimeCircle(i == 0);\n            weekView.setEnableCurrentTimeMarker(true);\n            weekView.setAvailabilityCalendar(resource.getAvailabilityCalendar());\n\n            weekView.installDefaultLassoFinishedBehaviour();\n\n            weekView.numberOfDaysProperty().bindBidirectional(resourcesView.numberOfDaysProperty());\n\n            Bindings.bindContent(weekView.getCalendarSources(), resource.getCalendarSources());\n            weekView.setDefaultCalendarProvider(dateControl -> resource.getCalendars().get(0));\n\n            this.box.getChildren().add(weekView);\n\n            if (i < resources.size() - 1) {\n                Callback<ResourcesView<T>, Region> separatorFactory = resourcesView.getLargeSeparatorFactory();\n                if (separatorFactory != null) {\n                    Region separator = separatorFactory.call(container.getResourcesView());\n                    if (separator != null) {\n                        this.box.getChildren().add(separator);\n                        HBox.setHgrow(separator, Priority.NEVER);\n                    }\n                }\n            }\n            HBox.setHgrow(weekView, Priority.ALWAYS);\n        }\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ResourcesViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Resource;\nimport com.calendarfx.util.ViewHelper;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.ResourcesView;\nimport com.calendarfx.view.ResourcesView.Type;\nimport com.calendarfx.view.TimeScaleView;\nimport com.calendarfx.view.WeekDayHeaderView;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Orientation;\nimport javafx.scene.Node;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.layout.VBox;\nimport javafx.util.Callback;\nimport org.controlsfx.control.PlusMinusSlider;\n\nimport java.time.LocalDate;\n\npublic class ResourcesViewSkin<T extends Resource<?>> extends DateControlSkin<ResourcesView<T>> {\n\n    private final GridPane gridPane;\n    private final ScrollBar scrollBar;\n    private final PlusMinusSlider plusMinusSlider;\n    private DayViewScrollPane dayViewScrollPane;\n    private final ResourcesViewContainer<T> resourcesViewContainer;\n    private final TimeScaleView timeScaleView;\n\n    public ResourcesViewSkin(ResourcesView<T> view) {\n        super(view);\n\n        scrollBar = new ScrollBar();\n        scrollBar.setOrientation(Orientation.VERTICAL);\n        scrollBar.visibleProperty().bind(view.scrollingEnabledProperty().not());\n\n        plusMinusSlider = new PlusMinusSlider();\n        plusMinusSlider.setOrientation(Orientation.VERTICAL);\n        plusMinusSlider.visibleProperty().bind(view.scrollingEnabledProperty());\n\n        plusMinusSlider.setOnValueChanged(evt -> {\n            // exponential function to increase scrolling speed when reaching ends of slider\n            final double base = plusMinusSlider.getValue();\n            final double pow = Math.signum(plusMinusSlider.getValue()) * Math.pow(base, 2);\n            final double pixel = pow * -100;\n            view.setScrollTime(view.getZonedDateTimeAt(0, pixel, view.getZoneId()));\n        });\n\n        resourcesViewContainer = new ResourcesViewContainer<>(view);\n        resourcesViewContainer.setMinHeight(0);\n        view.bind(resourcesViewContainer, true);\n\n        timeScaleView = new TimeScaleView();\n        view.bind(timeScaleView, true);\n\n        InvalidationListener updateViewListener = it -> updateView();\n        view.showAllDayViewProperty().addListener(updateViewListener);\n        view.showTimeScaleViewProperty().addListener(updateViewListener);\n        view.layoutProperty().addListener(updateViewListener);\n        view.showScrollBarProperty().addListener(updateViewListener);\n        view.showTimeScaleViewProperty().addListener(updateViewListener);\n        view.numberOfDaysProperty().addListener(updateViewListener);\n        view.typeProperty().addListener(updateViewListener);\n        view.getResources().addListener(updateViewListener);\n        view.scrollingEnabledProperty().addListener(updateViewListener);\n\n        RowConstraints row0 = new RowConstraints();\n        row0.setFillHeight(true);\n        row0.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row0.setVgrow(Priority.NEVER);\n\n        RowConstraints row1 = new RowConstraints();\n        row1.setFillHeight(true);\n        row1.setPrefHeight(Region.USE_COMPUTED_SIZE);\n        row1.setVgrow(Priority.ALWAYS);\n\n        gridPane = new GridPane();\n        gridPane.getRowConstraints().setAll(row0, row1);\n        gridPane.getStyleClass().add(\"container\");\n\n        getChildren().add(gridPane);\n\n        updateView();\n\n        /*\n         * Run later when the control has become visible.\n         */\n        Platform.runLater(() -> {\n            if (dayViewScrollPane != null) {\n                ViewHelper.scrollToRequestedTime(view, dayViewScrollPane);\n            }\n        });\n\n        view.requestedTimeProperty().addListener(it -> {\n            if (dayViewScrollPane != null) {\n                ViewHelper.scrollToRequestedTime(view, dayViewScrollPane);\n            }\n        });\n    }\n\n    private void updateView() {\n        gridPane.getChildren().clear();\n        gridPane.getColumnConstraints().clear();\n\n        dayViewScrollPane = null;\n\n        ResourcesView<T> resourcesView = getSkinnable();\n        if (resourcesView.getType().equals(Type.RESOURCES_OVER_DATES)) {\n            updateViewResourcesOverDates();\n        } else {\n            updateViewDatesOverResources();\n        }\n    }\n\n    private void updateViewDatesOverResources() {\n        final ResourcesView<T> resourcesView = getSkinnable();\n\n        if (resourcesView.isShowTimeScaleView()) {\n            ColumnConstraints timeScaleColumn = new ColumnConstraints();\n            timeScaleColumn.setFillWidth(true);\n            timeScaleColumn.setHgrow(Priority.NEVER);\n            gridPane.getColumnConstraints().add(timeScaleColumn);\n\n            if (resourcesView.isScrollingEnabled()) {\n                gridPane.add(timeScaleView, 0, 1);\n            } else {\n                DayViewScrollPane timeScaleScrollPane = new DayViewScrollPane(timeScaleView, scrollBar);\n                timeScaleScrollPane.getStyleClass().addAll(\"calendar-scroll-pane\", \"day-view-timescale-scroll-pane\");\n                timeScaleScrollPane.setMinWidth(Region.USE_PREF_SIZE);\n                gridPane.add(timeScaleScrollPane, 0, 1);\n            }\n\n            Node upperLeftCorner = resourcesView.getUpperLeftCorner();\n            upperLeftCorner.getStyleClass().add(\"upper-left-corner\");\n            gridPane.add(upperLeftCorner, 0, 0);\n        }\n\n        if (resourcesView.isShowScrollBar()) {\n            Node upperRightCorner = resourcesView.getUpperRightCorner();\n            upperRightCorner.getStyleClass().add(\"upper-right-corner\");\n            gridPane.add(upperRightCorner, 2, 0);\n        }\n\n        Callback<T, Node> resourceHeaderFactory = resourcesView.getResourceHeaderFactory();\n\n        ObservableList<T> resources = resourcesView.getResources();\n\n        HBox headerBox = new HBox();\n        headerBox.getStyleClass().add(\"header-box\");\n\n        gridPane.add(headerBox, 1, 0);\n\n        for (int dayIndex = 0; dayIndex < resourcesView.getNumberOfDays(); dayIndex++) {\n            ObjectProperty<LocalDate> dateProperty = new SimpleObjectProperty<>(this, \"date\");\n            final int additionalDays = dayIndex;\n            dateProperty.bind(Bindings.createObjectBinding(() -> resourcesView.getDate().plusDays(additionalDays), resourcesView.dateProperty()));\n\n            VBox dayBox = new VBox();\n            dayBox.getStyleClass().add(\"day-box\");\n            HBox.setHgrow(dayBox, Priority.ALWAYS);\n\n            WeekDayHeaderView weekDayHeaderView = new WeekDayHeaderView(resourcesView.getNumberOfDays());\n            resourcesView.bind(weekDayHeaderView, false);\n\n            weekDayHeaderView.dateProperty().bind(dateProperty);\n            weekDayHeaderView.setNumberOfDays(1);\n            weekDayHeaderView.setAdjustToFirstDayOfWeek(false);\n\n            weekDayHeaderView.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n            if (resourcesView.getNumberOfDays() == 1) {\n                weekDayHeaderView.getStyleClass().add(\"only\");\n            } else {\n                if (dayIndex == 0) {\n                    weekDayHeaderView.getStyleClass().add(\"first\");\n                } else if (dayIndex == resourcesView.getNumberOfDays() - 1) {\n                    weekDayHeaderView.getStyleClass().add(\"last\");\n                } else {\n                    weekDayHeaderView.getStyleClass().add(\"middle\");\n                }\n            }\n\n            dayBox.getChildren().add(weekDayHeaderView);\n\n            HBox allResourcesBox = new HBox();\n            VBox.setVgrow(allResourcesBox, Priority.ALWAYS);\n\n            dayBox.getChildren().add(allResourcesBox);\n\n            headerBox.getChildren().add(dayBox);\n\n            // separator between dates\n            if (dayIndex < resourcesView.getNumberOfDays() - 1) {\n                Callback<ResourcesView<T>, Region> separatorFactory = resourcesView.getLargeSeparatorFactory();\n                if (separatorFactory != null) {\n                    Region separator = separatorFactory.call(resourcesView);\n                    if (separator != null) {\n                        headerBox.getChildren().add(separator);\n                        HBox.setHgrow(separator, Priority.NEVER);\n                    }\n                }\n            }\n\n            for (int resourceIndex = 0; resourceIndex < resources.size(); resourceIndex++) {\n                T resource = resources.get(resourceIndex);\n\n                Node resourceHeaderNode = resourceHeaderFactory.call(resource);\n\n                VBox singleResourceBox = new VBox(resourceHeaderNode);\n                HBox.setHgrow(singleResourceBox, Priority.ALWAYS);\n\n                allResourcesBox.getChildren().add(singleResourceBox);\n\n                resourceHeaderNode.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n                if (resources.size() == 1) {\n                    resourceHeaderNode.getStyleClass().add(\"only\");\n                } else {\n                    if (resourceIndex == 0) {\n                        resourceHeaderNode.getStyleClass().add(\"first\");\n                    } else if (resourceIndex == resources.size() - 1) {\n                        resourceHeaderNode.getStyleClass().add(\"last\");\n                    } else {\n                        resourceHeaderNode.getStyleClass().add(\"middle\");\n                    }\n                }\n\n                if (resourcesView.isShowAllDayView()) {\n\n                    Callback<T, AllDayView> allDayViewFactory = resourcesView.getAllDayViewFactory();\n                    AllDayView allDayView = allDayViewFactory.call(resource);\n\n                    allDayView.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n                    if (resources.size() == 1) {\n                        allDayView.getStyleClass().add(\"only\");\n                    } else {\n                        if (resourceIndex == 0) {\n                            allDayView.getStyleClass().add(\"first\");\n                        } else if (resourceIndex == resources.size() - 1) {\n                            allDayView.getStyleClass().add(\"last\");\n                        } else {\n                            allDayView.getStyleClass().add(\"middle\");\n                        }\n                    }\n\n                    // bind AllDayView\n                    resourcesView.bind(allDayView, false);\n\n                    Bindings.unbindBidirectional(resourcesView.adjustToFirstDayOfWeekProperty(), allDayView.adjustToFirstDayOfWeekProperty());\n                    Bindings.unbindBidirectional(resourcesView.numberOfDaysProperty(), allDayView.numberOfDaysProperty());\n\n                    allDayView.dateProperty().bind(dateProperty);\n                    allDayView.setAdjustToFirstDayOfWeek(false);\n                    allDayView.setNumberOfDays(1);\n\n                    // some un-bindings for AllDayView\n                    Bindings.unbindBidirectional(resourcesView.defaultCalendarProviderProperty(), allDayView.defaultCalendarProviderProperty());\n                    Bindings.unbindBidirectional(resourcesView.draggedEntryProperty(), allDayView.draggedEntryProperty());\n                    Bindings.unbindContentBidirectional(resourcesView.getCalendarSources(), allDayView.getCalendarSources());\n\n                    Bindings.bindContent(allDayView.getCalendarSources(), resource.getCalendarSources());\n\n                    VBox.setVgrow(allDayView, Priority.ALWAYS);\n                    singleResourceBox.getChildren().add(allDayView);\n                }\n\n                singleResourceBox.setPrefWidth(0); // so they all end up with the same percentage width\n\n                HBox.setHgrow(singleResourceBox, Priority.ALWAYS);\n\n                if (resourceIndex < resources.size() - 1) {\n                    Callback<ResourcesView<T>, Region> separatorFactory = resourcesView.getSmallSeparatorFactory();\n                    if (separatorFactory != null) {\n                        Region separator = separatorFactory.call(resourcesView);\n                        if (separator != null) {\n                            allResourcesBox.getChildren().add(separator);\n                            HBox.setHgrow(separator, Priority.NEVER);\n                        }\n                    }\n                }\n            }\n        }\n\n        ColumnConstraints containerOrScrollPaneConstraints = new ColumnConstraints();\n        containerOrScrollPaneConstraints.setFillWidth(true);\n        containerOrScrollPaneConstraints.setHgrow(Priority.ALWAYS);\n        gridPane.getColumnConstraints().add(containerOrScrollPaneConstraints);\n\n        if (resourcesView.isScrollingEnabled()) {\n            gridPane.add(resourcesViewContainer, 1, 1);\n        } else {\n            dayViewScrollPane = new DayViewScrollPane(resourcesViewContainer, scrollBar);\n            gridPane.add(dayViewScrollPane, 1, 1);\n        }\n\n        if (resourcesView.isShowScrollBar()) {\n            ColumnConstraints scrollbarConstraint = new ColumnConstraints();\n            scrollbarConstraint.setFillWidth(true);\n            scrollbarConstraint.setHgrow(Priority.NEVER);\n            scrollbarConstraint.setPrefWidth(Region.USE_COMPUTED_SIZE);\n\n            gridPane.getColumnConstraints().add(scrollbarConstraint);\n\n            if (resourcesView.isScrollingEnabled()) {\n                gridPane.add(plusMinusSlider, 2, 1);\n            } else {\n                gridPane.add(scrollBar, 2, 1);\n            }\n        }\n    }\n\n    private void updateViewResourcesOverDates() {\n        final ResourcesView<T> resourcesView = getSkinnable();\n\n        if (resourcesView.isShowTimeScaleView()) {\n            ColumnConstraints timeScaleColumn = new ColumnConstraints();\n            timeScaleColumn.setFillWidth(true);\n            timeScaleColumn.setHgrow(Priority.NEVER);\n            gridPane.getColumnConstraints().add(timeScaleColumn);\n\n            if (resourcesView.isScrollingEnabled()) {\n                gridPane.add(timeScaleView, 0, 1);\n            } else {\n                // time scale scroll pane\n                DayViewScrollPane timeScaleScrollPane = new DayViewScrollPane(timeScaleView, scrollBar);\n                timeScaleScrollPane.getStyleClass().addAll(\"calendar-scroll-pane\", \"day-view-timescale-scroll-pane\");\n                timeScaleScrollPane.setMinWidth(Region.USE_PREF_SIZE);\n                gridPane.add(timeScaleScrollPane, 0, 1);\n            }\n\n            Node upperLeftCorner = resourcesView.getUpperLeftCorner();\n            upperLeftCorner.getStyleClass().add(\"upper-left-corner\");\n            gridPane.add(upperLeftCorner, 0, 0);\n        }\n\n        if (resourcesView.isShowScrollBar()) {\n            Node upperRightCorner = resourcesView.getUpperRightCorner();\n            upperRightCorner.getStyleClass().add(\"upper-right-corner\");\n            gridPane.add(upperRightCorner, 2, 0);\n        }\n\n        HBox headerBox = new HBox();\n        headerBox.getStyleClass().add(\"header-box\");\n\n        gridPane.add(headerBox, 1, 0);\n\n        Callback<T, Node> resourceHeaderFactory = resourcesView.getResourceHeaderFactory();\n\n        ObservableList<T> resources = resourcesView.getResources();\n        for (int i = 0; i < resources.size(); i++) {\n            T resource = resources.get(i);\n\n            Node headerNode = resourceHeaderFactory.call(resource);\n\n            VBox resourceHeader = new VBox(headerNode);\n            resourceHeader.getStyleClass().removeAll(\"only\", \"first\", \"middle\", \"last\");\n\n            if (resources.size() == 1) {\n                resourceHeader.getStyleClass().add(\"only\");\n            } else {\n                if (i == 0) {\n                    resourceHeader.getStyleClass().add(\"first\");\n                } else if (i == resources.size() - 1) {\n                    resourceHeader.getStyleClass().add(\"last\");\n                } else {\n                    resourceHeader.getStyleClass().add(\"middle\");\n                }\n            }\n\n            if (resourcesView.isShowAllDayView()) {\n                AllDayView allDayView = new AllDayView();\n                allDayView.setAdjustToFirstDayOfWeek(false);\n\n                // bind AllDayView\n                resourcesView.bind(allDayView, true);\n                allDayView.numberOfDaysProperty().bind(resourcesView.numberOfDaysProperty());\n\n                // rebind\n                allDayView.adjustToFirstDayOfWeekProperty().bind(resourcesView.adjustToFirstDayOfWeekProperty());\n\n                // some unbindings for AllDayView\n                Bindings.unbindBidirectional(resourcesView.defaultCalendarProviderProperty(), allDayView.defaultCalendarProviderProperty());\n                Bindings.unbindBidirectional(resourcesView.draggedEntryProperty(), allDayView.draggedEntryProperty());\n                Bindings.unbindContentBidirectional(resourcesView.getCalendarSources(), allDayView.getCalendarSources());\n\n                Bindings.bindContent(allDayView.getCalendarSources(), resource.getCalendarSources());\n                resourceHeader.getChildren().add(allDayView);\n            }\n\n            resourceHeader.getStyleClass().add(\"resource-header-view\");\n\n            WeekDayHeaderView weekDayHeaderView = resourcesView.getWeekDayHeaderViewFactory().call(resource);\n            weekDayHeaderView.adjustToFirstDayOfWeekProperty().bind(resourcesView.adjustToFirstDayOfWeekProperty());\n            weekDayHeaderView.numberOfDaysProperty().bind(resourcesView.numberOfDaysProperty());\n            resourcesView.bind(weekDayHeaderView, true);\n\n            resourceHeader.setPrefWidth(0); // so they all end up with the same percentage width\n            resourceHeader.getChildren().add(weekDayHeaderView);\n\n            headerBox.getChildren().add(resourceHeader);\n            HBox.setHgrow(resourceHeader, Priority.ALWAYS);\n\n            if (i < resources.size() - 1) {\n                Callback<ResourcesView<T>, Region> separatorFactory = resourcesView.getLargeSeparatorFactory();\n                if (separatorFactory != null) {\n                    Region separator = separatorFactory.call(resourcesView);\n                    if (separator != null) {\n                        headerBox.getChildren().add(separator);\n                        HBox.setHgrow(separator, Priority.NEVER);\n                    }\n                }\n            }\n        }\n\n        ColumnConstraints dayViewsConstraints = new ColumnConstraints();\n        dayViewsConstraints.setFillWidth(true);\n        dayViewsConstraints.setHgrow(Priority.ALWAYS);\n        gridPane.getColumnConstraints().add(dayViewsConstraints);\n\n        if (resourcesView.isScrollingEnabled()) {\n            resourcesViewContainer.setTranslateY(0);\n            resourcesViewContainer.setManaged(true);\n            gridPane.add(resourcesViewContainer, 1, 1);\n        } else {\n            dayViewScrollPane = new DayViewScrollPane(resourcesViewContainer, scrollBar);\n            gridPane.add(dayViewScrollPane, 1, 1);\n        }\n\n        if (resourcesView.isShowScrollBar()) {\n            ColumnConstraints scrollbarConstraint = new ColumnConstraints();\n            scrollbarConstraint.setFillWidth(true);\n            scrollbarConstraint.setHgrow(Priority.NEVER);\n            scrollbarConstraint.setPrefWidth(Region.USE_COMPUTED_SIZE);\n            gridPane.getColumnConstraints().add(scrollbarConstraint);\n\n            gridPane.add(scrollBar, 2, 1);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/SearchResultViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.RequestEvent;\nimport com.calendarfx.view.SearchResultView;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.shape.Circle;\nimport javafx.util.Callback;\n\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\n\n@SuppressWarnings(\"javadoc\")\npublic class SearchResultViewSkin extends SkinBase<SearchResultView> {\n\n    private final ListView<Entry<?>> listView;\n\n    public SearchResultViewSkin(SearchResultView view) {\n        super(view);\n\n        Label placeholderLabel = new Label();\n        placeholderLabel.getStyleClass().add(\"placeholder-label\");\n\n        listView = new ListView<>();\n        listView.setItems(view.getSearchResults());\n        listView.setCellFactory(lv -> new SearchResultListViewCell());\n        listView.setPlaceholder(placeholderLabel);\n        listView.getSelectionModel().selectedItemProperty().addListener(it -> view.getProperties().put(\"selected.search.result\", listView.getSelectionModel().getSelectedItem()));\n        getChildren().add(listView);\n    }\n\n    public static class SearchResultListViewCell extends ListCell<Entry<?>> {\n\n        private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);\n        private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n\n        private final Circle colorCircle;\n        private final Label titleLabel;\n        private final Label dateLabel;\n        private final Label timeLabel;\n        private final BorderPane borderPane;\n\n        public SearchResultListViewCell() {\n            setPrefWidth(0);\n\n            getStyleClass().add(\"search-result-cell\");\n\n            colorCircle = new Circle();\n            colorCircle.setRadius(3.5);\n\n            titleLabel = new Label();\n            titleLabel.setMinWidth(0);\n            titleLabel.setGraphic(colorCircle);\n            titleLabel.getStyleClass().add(\"title-label\");\n\n            dateLabel = new Label();\n            dateLabel.setMinWidth(0);\n            dateLabel.getStyleClass().add(\"date-label\");\n\n            timeLabel = new Label();\n            timeLabel.setMinWidth(0);\n            timeLabel.getStyleClass().add(\"time-label\");\n\n            BorderPane dateTimePane = new BorderPane();\n            dateTimePane.getStyleClass().add(\"date-time-pane\");\n            dateTimePane.setLeft(dateLabel);\n            dateTimePane.setRight(timeLabel);\n\n            borderPane = new BorderPane();\n            borderPane.getStyleClass().add(\"container\");\n            borderPane.setTop(titleLabel);\n            borderPane.setBottom(dateTimePane);\n\n            setGraphic(borderPane);\n            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n\n            setOnMouseClicked(evt -> {\n                if (evt.getClickCount() == 2) {\n                    Entry<?> entry = getItem();\n                    if (entry != null) {\n                        fireEvent(new RequestEvent(this, this, entry));\n                    }\n                }\n            });\n        }\n\n        @Override\n        protected void updateItem(Entry<?> entry, boolean empty) {\n            super.updateItem(entry, empty);\n            if (entry != null) {\n                Calendar calendar = entry.getCalendar();\n\n                borderPane.setVisible(true);\n                colorCircle.getStyleClass().add(calendar.getStyle() + \"-icon\");\n\n                titleLabel.setText(entry.getTitle());\n                titleLabel.setVisible(true);\n                timeLabel.setText(getTimeText(entry));\n                dateLabel.setText(dateFormatter.format(entry.getStartDate()));\n            } else {\n                borderPane.setVisible(false);\n            }\n        }\n\n        private String getTimeText(Entry<?> entry) {\n            if (entry.isFullDay()) {\n                return \"all-day\";\n            }\n\n            LocalDate startDate = entry.getStartDate();\n            LocalDate endDate = entry.getEndDate();\n\n            String text;\n            if (startDate.equals(endDate)) {\n                text = MessageFormat.format(Messages.getString(\"SearchResultViewSkin.FROM_UNTIL\"),\n                        timeFormatter.format(entry.getStartTime()),\n                        timeFormatter.format(entry.getEndTime()));\n            } else {\n                text = MessageFormat.format(Messages.getString(\"SearchResultViewSkin.FROM_UNTIL_WITH_DATE\"),\n                        timeFormatter.format(entry.getStartTime()),\n                        dateFormatter.format(entry.getStartDate()),\n                        timeFormatter.format(entry.getEndTime()),\n                        dateFormatter.format(entry.getEndDate()));\n            }\n\n            return text;\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/SourceGridViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.SourceGridView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.WeakInvalidationListener;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.WeakListChangeListener;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.VBox;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SourceGridViewSkin extends SkinBase<SourceGridView> {\n\n    private static final String DEFAULT_STYLE = \"source-grid-view\";\n\n    private final InvalidationListener buildListener = obs -> build();\n    private final WeakInvalidationListener buildWeakListener = new WeakInvalidationListener(buildListener);\n\n    private final ListChangeListener<CalendarSource> sourcesChangeListener = c -> {\n        while (c.next()) {\n            for (CalendarSource added : c.getAddedSubList()) {\n                added.getCalendars().addListener(buildWeakListener);\n            }\n\n            for (CalendarSource removed : c.getRemoved()) {\n                removed.getCalendars().removeListener(buildWeakListener);\n            }\n        }\n\n        build();\n    };\n    private final WeakListChangeListener<CalendarSource> sourcesWeakChangeListener = new WeakListChangeListener<>(sourcesChangeListener);\n\n    private final HBox container;\n\n    public SourceGridViewSkin(SourceGridView control) {\n        super(control);\n\n        container = new HBox();\n        container.getStyleClass().add(DEFAULT_STYLE);\n        build();\n\n        control.getCalendarSources().addListener(sourcesWeakChangeListener);\n        control.maximumRowsPerColumnProperty().addListener(buildWeakListener);\n        getChildren().add(container);\n    }\n\n    private void build() {\n        List<Node> children = new ArrayList<>();\n\n        VBox row = null;\n        int count = 0;\n\n        final SourceGridView view = getSkinnable();\n\n        for (CalendarSource source : view.getCalendarSources()) {\n            for (Calendar calendar : source.getCalendars()) {\n\n                view.getCalendarVisibilityProperty(calendar).removeListener(buildWeakListener);\n                view.getCalendarVisibilityProperty(calendar).addListener(buildWeakListener);\n\n                if (!view.isCalendarVisible(calendar)) {\n                    continue;\n                }\n\n                if (count == 0) {\n                    row = new VBox();\n                    row.getStyleClass().add(\"column\");\n                    children.add(row);\n                }\n\n                row.getChildren().add(new CalendarItem(calendar));\n                count++;\n\n                if (count == view.getMaximumRowsPerColumn()) {\n                    count = 0;\n                }\n            }\n        }\n\n        container.getChildren().setAll(children);\n    }\n\n    private static class CalendarItem extends Label {\n\n        public CalendarItem(Calendar calendar) {\n            Pane graphic = new Pane();\n            graphic.getStyleClass().add(\"item-box\");\n            graphic.getStyleClass().add(calendar.getStyle() + \"-source-grid-item-box\");\n\n            getStyleClass().add(\"item\");\n            setGraphic(graphic);\n            setText(calendar.getName());\n        }\n\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/SourceViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.SourceView;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.binding.Bindings;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.VBox;\n\n@SuppressWarnings(\"javadoc\")\npublic class SourceViewSkin extends SkinBase<SourceView> {\n\n    private final VBox vbox;\n\n    private final InvalidationListener updater = obs -> updateView();\n\n    public SourceViewSkin(SourceView view) {\n        super(view);\n\n        vbox = new VBox();\n        vbox.getStyleClass().add(\"container\");\n\n        getChildren().add(vbox);\n\n        view.getCalendarSources().addListener(updater);\n        updateView();\n    }\n\n    private void updateView() {\n        vbox.getChildren().clear();\n        for (CalendarSource source : getSkinnable().getCalendarSources()) {\n            source.getCalendars().removeListener(updater);\n            source.getCalendars().addListener(updater);\n\n            VBox box = new VBox(8);\n            box.getStyleClass().add(\"single-calendar-group\");\n\n            for (Calendar calendar : source.getCalendars()) {\n                CheckBox checkBox = new CheckBox();\n                checkBox.textProperty().bind(calendar.nameProperty());\n                checkBox.getStyleClass().addAll(\"default-style-visibility-checkbox\",\n                        calendar.getStyle() + \"-visibility-checkbox\");\n                Bindings.bindBidirectional(checkBox.selectedProperty(), getSkinnable().getCalendarVisibilityProperty(calendar));\n                box.getChildren().add(checkBox);\n            }\n\n            if (getSkinnable().getCalendarSources().size() == 1) {\n                vbox.getChildren().add(box);\n            } else {\n                TitledPane titledPane = new TitledPane();\n                titledPane.textProperty().bind(source.nameProperty());\n                titledPane.setContent(box);\n                vbox.getChildren().add(titledPane);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/TimeFieldSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.TimeField;\nimport javafx.beans.InvalidationListener;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.TextFormatter;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.layout.HBox;\nimport javafx.util.StringConverter;\n\nimport java.time.LocalTime;\n\n@SuppressWarnings(\"javadoc\")\npublic class TimeFieldSkin extends SkinBase<TimeField> {\n\n    private final NumericTextField hourField;\n    private final NumericTextField minuteField;\n\n    public TimeFieldSkin(TimeField field) {\n        super(field);\n\n        StringConverter<String> valueConverter = new StringConverter<>() {\n\n            @Override\n            public String fromString(String text) {\n                if (text.length() == 0) {\n                    return \"00\";\n                } else if (text.length() == 1) {\n                    return \"0\" + text;\n                }\n\n                return text;\n            }\n\n            @Override\n            public String toString(String text) {\n                return text;\n            }\n        };\n\n        TextFormatter<String> hourFormatter = new TextFormatter<>(valueConverter, \"0\");\n        TextFormatter<String> minuteFormatter = new TextFormatter<>(valueConverter, \"0\");\n\n        hourField = new NumericTextField(23);\n        hourField.setOnKeyPressed(new RollingHandler(hourField, 23));\n        hourField.setTextFormatter(hourFormatter);\n\n        minuteField = new NumericTextField(59);\n        minuteField.setOnKeyPressed(new RollingHandler(minuteField, 59));\n        minuteField.setTextFormatter(minuteFormatter);\n\n        Label separator = new Label(\":\");\n        separator.setMaxHeight(Double.MAX_VALUE);\n\n        HBox box = new HBox();\n        box.getStyleClass().add(\"box\");\n        box.setFillHeight(true);\n        box.getChildren().addAll(hourField, separator, minuteField);\n\n        getChildren().add(box);\n\n        field.valueProperty().addListener(it -> {\n            if (!updatingValue) {\n                updateFields();\n            }\n        });\n\n        updateFields();\n\n        // Install the listener after setting the values, avoid unnecessary\n        // notifications\n        InvalidationListener updateValueListener = it -> {\n            if (!updatingTextFields) {\n                updateValue();\n            }\n        };\n\n        hourField.textProperty().addListener(updateValueListener);\n        minuteField.textProperty().addListener(updateValueListener);\n    }\n\n    private boolean updatingTextFields;\n\n    private void updateFields() {\n        updatingTextFields = true;\n\n        try {\n            TimeField timeField = getSkinnable();\n            LocalTime localTime = timeField.getValue();\n            if (localTime != null) {\n                hourField.setText(Integer.toString(localTime.getHour()));\n                minuteField.setText(Integer.toString(localTime.getMinute()));\n            } else {\n                hourField.setText(\"\");\n                minuteField.setText(\"\");\n            }\n        } finally {\n            updatingTextFields = false;\n        }\n    }\n\n    private boolean updatingValue;\n\n    private void updateValue() {\n        updatingValue = true;\n        try {\n            int hour = 0;\n            int minute = 0;\n            try {\n                hour = Math.max(0, Math.min(23, Integer.parseInt(hourField.getText())));\n                minute = Math.max(0, Math.min(59, Integer.parseInt(minuteField.getText())));\n            } catch (NumberFormatException ex) {\n                // do nothing\n            }\n\n            /*\n             * LocalTime is immutable, hence we have to create new instances over\n             * and over again, which causes property change events. So we have to\n             * have this check here to ensure that the value has really changed. And\n             * we only care about hours and minutes, so we are not using\n             * LocalTime.equals().\n             */\n            LocalTime oldTime = getSkinnable().getValue();\n            LocalTime newTime = LocalTime.of(hour, minute);\n\n            if (oldTime != null && newTime != null) {\n                if (!(oldTime.getHour() == newTime.getHour() && oldTime.getMinute() == newTime.getMinute())) {\n                    getSkinnable().setValue(newTime);\n                }\n            } else if (newTime == null) {\n                getSkinnable().setValue(null);\n\n            }\n        } finally {\n            updatingValue = false;\n        }\n    }\n\n    public class RollingHandler implements EventHandler<KeyEvent> {\n\n        private final int max;\n        private final TextField field;\n\n        public RollingHandler(TextField field, int max) {\n            this.field = field;\n            this.max = max;\n        }\n\n        @Override\n        public void handle(KeyEvent event) {\n            switch (event.getCode()) {\n                case UP:\n                    increment();\n                    break;\n                case DOWN:\n                    decrement();\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private void increment() {\n            int value = 0;\n            try {\n                value = Integer.parseInt(field.getText());\n            } catch (NumberFormatException ex) {\n                // do nothing\n            }\n            if (value < max) {\n                value++;\n            } else if (getSkinnable().isRollOver()) {\n                value = 0;\n            }\n            if (value < 10) {\n                field.setText(\"0\" + value);\n            } else {\n                field.setText(Integer.toString(value));\n            }\n        }\n\n        private void decrement() {\n            int value = 0;\n            try {\n                value = Integer.parseInt(field.getText());\n            } catch (NumberFormatException ex) {\n                // do nothing\n            }\n            if (value > 0) {\n                value--;\n            } else if (getSkinnable().isRollOver()) {\n                value = max;\n            }\n\n            if (value < 10) {\n                field.setText(\"0\" + value);\n            } else {\n                field.setText(Integer.toString(value));\n            }\n        }\n    }\n\n    public class NumericTextField extends TextField {\n\n        public NumericTextField(int max) {\n            setPrefColumnCount(2);\n            focusedProperty().addListener(it -> {\n                if (isFocused()) {\n                    selectAll();\n                }\n            });\n        }\n\n        @Override\n        public void replaceText(int start, int end, String s) {\n            super.replaceText(start, end, s.replaceAll(\"[^0-9]\", \"\"));\n        }\n\n        @Override\n        public void replaceSelection(String s) {\n            super.replaceSelection(s.replaceAll(\"[^0-9]\", \"\"));\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/TimeScaleViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.util.ViewHelper;\nimport com.calendarfx.view.TimeScaleView;\nimport javafx.animation.FadeTransition;\nimport javafx.beans.value.ChangeListener;\nimport javafx.geometry.BoundingBox;\nimport javafx.geometry.Bounds;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.OverrunStyle;\nimport javafx.scene.shape.Rectangle;\nimport javafx.util.Duration;\n\nimport java.time.Instant;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TimeScaleViewSkin<T extends TimeScaleView> extends DayViewBaseSkin<T> {\n\n    private static final String EARLY_HOUR_LABEL = \"early-hour-label\";\n    private static final String LATE_HOUR_LABEL = \"late-hour-label\";\n\n    private final List<Label> timeLabels = new ArrayList<>();\n\n    private final Label currentTimeLabel;\n\n    public TimeScaleViewSkin(T view) {\n        super(view);\n\n        currentTimeLabel = new Label();\n        currentTimeLabel.getStyleClass().add(\"current-time-label\");\n        currentTimeLabel.setManaged(false);\n        currentTimeLabel.setMaxWidth(Double.MAX_VALUE);\n        currentTimeLabel.setAlignment(Pos.CENTER_RIGHT);\n        currentTimeLabel.setOpacity(0);\n        currentTimeLabel.setTextOverrun(OverrunStyle.CLIP);\n        currentTimeLabel.visibleProperty().bind(view.enableCurrentTimeMarkerProperty());\n\n        getChildren().add(currentTimeLabel);\n\n        updateCurrentTimeMarkerVisibility();\n        view.showCurrentTimeMarkerProperty().addListener(it -> updateCurrentTimeMarkerVisibility());\n        setupCurrentTimeMarkerSupport();\n        updateShowMarkers();\n\n        view.scrollingEnabledProperty().addListener(it -> {\n            timeLabels.clear();\n            getChildren().clear();\n            view.requestLayout();\n        });\n\n        view.scrollTimeProperty().addListener(it -> {\n            if (view.isScrollingEnabled()) {\n                view.requestLayout();\n            }\n        });\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(view.widthProperty());\n        clip.heightProperty().bind(view.heightProperty());\n        view.setClip(clip);\n    }\n\n    private Label createTimeLabel() {\n        Label label = new Label();\n        label.setManaged(false);\n        label.setMaxWidth(Double.MAX_VALUE);\n        label.setAlignment(Pos.CENTER_RIGHT);\n        label.setTextOverrun(OverrunStyle.CLIP);\n        timeLabels.add(label);\n        getChildren().add(label);\n        return label;\n    }\n\n    private void updateCurrentTimeMarkerVisibility() {\n        double opacity = getSkinnable().isShowCurrentTimeMarker() ? 1 : 0;\n\n        FadeTransition lineTransition = new FadeTransition(Duration.millis(600), currentTimeLabel);\n        lineTransition.setToValue(opacity);\n        lineTransition.play();\n\n        /*\n         * Need to re-layout as regular time labels might be invisible because\n         * they were intersecting with the current time label.\n         */\n        getSkinnable().requestLayout();\n    }\n\n    protected void setupCurrentTimeMarkerSupport() {\n        T view = getSkinnable();\n        // do not use an invalidation listener as that will also fire for same dates\n        ChangeListener listener = (obs, oldValue, newValue) -> updateShowMarkers();\n        view.dateProperty().addListener(listener);\n        view.todayProperty().addListener(listener);\n    }\n\n    private void updateShowMarkers() {\n        T view = getSkinnable();\n        view.getProperties().put(\"show.current.time.marker\", isShowingTimeMarker());\n    }\n\n    protected boolean isShowingTimeMarker() {\n        return getSkinnable().getDate().equals(getSkinnable().getToday());\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        super.layoutChildren(contentX, contentY, contentWidth, contentHeight);\n\n        if (getSkinnable().isScrollingEnabled()) {\n            layoutChildrenInfiniteScrolling(contentX, contentY, contentWidth, contentHeight);\n        } else {\n            layoutChildrenStatic(contentX, contentY, contentWidth, contentHeight);\n        }\n    }\n\n    private void layoutChildrenInfiniteScrolling(double contentX, double contentY, double contentWidth, double contentHeight) {\n        final T view = getSkinnable();\n        final ZonedDateTime scrollTime = view.getScrollTime();\n\n        Instant time = scrollTime.withHour(0).toInstant().truncatedTo(ChronoUnit.HOURS);\n\n        double y = view.getLocation(time);\n\n        int index = 0;\n\n        Bounds lastBoundsUsed = null;\n\n        do {\n            final LocalTime localTime = LocalTime.ofInstant(time, view.getZoneId());\n            boolean midnight = localTime.equals(LocalTime.MIN);\n\n            Label label;\n\n            if (index < timeLabels.size()) {\n                label = timeLabels.get(index);\n            } else {\n                label = createTimeLabel();\n            }\n\n            if (midnight) {\n                label.getStyleClass().setAll(\"label\", \"date-label\");\n            } else {\n                label.getStyleClass().setAll(\"label\", \"time-label\");\n            }\n\n            double prefHeight = label.prefHeight(contentWidth);\n\n            if (midnight) {\n                ZonedDateTime dateTime = ZonedDateTime.ofInstant(time, view.getZoneId());\n                label.setText(dateTime.toLocalDate().format(view.getDateFormatter()));\n                label.setStyle(view.getDateStyleProvider().apply(dateTime.toLocalDateTime()));\n            } else {\n                ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(time, view.getZoneId());\n                label.setText(zonedDateTime.toLocalTime().format(view.getTimeFormatter()));\n                label.setStyle(view.getTimeStyleProvider().apply(zonedDateTime.toLocalDateTime()));\n            }\n\n            final BoundingBox layoutBounds = new BoundingBox(snapPositionX(contentX), snapPositionY(y - prefHeight / 2), snapSizeX(contentWidth), snapSizeY(prefHeight));\n\n            boolean labelVisible = lastBoundsUsed == null || !layoutBounds.intersects(lastBoundsUsed);\n\n            if (!labelVisible) {\n\n                if (midnight) {\n                    labelVisible = true;\n                }\n\n            }\n            label.setVisible(labelVisible);\n\n            if (labelVisible) {\n                label.resizeRelocate(layoutBounds.getMinX(), layoutBounds.getMinY(), layoutBounds.getWidth(), layoutBounds.getHeight());\n                lastBoundsUsed = layoutBounds;\n            }\n\n\n            index++;\n\n            time = time.plus(60, ChronoUnit.MINUTES);\n            y = view.getLocation(time);\n\n        } while (y < contentY + contentHeight);\n\n        for (int i = index; i < timeLabels.size(); i++) {\n            timeLabels.get(i).setVisible(false);\n        }\n    }\n\n    private void layoutChildrenStatic(double contentX, double contentY, double contentWidth, double contentHeight) {\n        // now label\n        LocalTime now = getSkinnable().getTime();\n        currentTimeLabel.setText(now.format(getSkinnable().getTimeFormatter()));\n        placeLabel(currentTimeLabel, now, contentX, contentY, contentWidth, contentHeight);\n\n        // hour labels\n        LocalTime startTime = getSkinnable().getStartTime();\n        LocalTime endTime = getSkinnable().getEndTime();\n\n        for (int hour = 0; hour < 23; hour++) {\n            LocalTime time = LocalTime.of(hour + 1, 0);\n            Label label;\n\n            if (hour < timeLabels.size()) {\n                label = timeLabels.get(hour);\n            } else {\n                label = createTimeLabel();\n                label.getStyleClass().add(\"time-label\");\n            }\n\n            label.getStyleClass().removeAll(EARLY_HOUR_LABEL, LATE_HOUR_LABEL);\n            label.setStyle(getSkinnable().getTimeStyleProvider().apply(time.atDate(getSkinnable().getDate())));\n\n            placeLabel(label, time, contentX, contentY, contentWidth, contentHeight);\n\n            Bounds localToParent1 = currentTimeLabel.localToParent(currentTimeLabel.getLayoutBounds());\n            Bounds localToParent2 = label.localToParent(label.getLayoutBounds());\n\n            label.setVisible(!currentTimeLabel.isVisible() || !getSkinnable().isShowCurrentTimeMarker() || !localToParent1.intersects(localToParent2));\n\n            if (time.isBefore(startTime) && !label.getStyleClass().contains(EARLY_HOUR_LABEL)) {\n                label.getStyleClass().add(EARLY_HOUR_LABEL);\n            }\n            if (time.isAfter(endTime) && !label.getStyleClass().contains(LATE_HOUR_LABEL)) {\n                label.getStyleClass().add(LATE_HOUR_LABEL);\n            }\n\n            if (label.isVisible()) {\n                switch (getSkinnable().getEarlyLateHoursStrategy()) {\n                    case HIDE:\n                    case SHOW_COMPRESSED:\n                        if (time.isBefore(startTime) || time.isAfter(endTime)) {\n                            label.setVisible(false);\n                        }\n                        break;\n                    case SHOW:\n                        label.setVisible(true);\n                        break;\n                    default:\n                        break;\n\n                }\n            }\n        }\n\n        currentTimeLabel.toFront();\n    }\n\n    private void placeLabel(Label label, LocalTime time, double contentX, double contentY, double contentWidth, double contentHeight) {\n\n        double prefHeight = label.prefHeight(contentWidth);\n\n        double y = contentY + ViewHelper.getTimeLocation(getSkinnable(), time, true);\n\n        /*\n         * Min and max calculations to ensure text is completely visible at the\n         * top and the bottom.\n         */\n        y = Math.min(contentHeight - label.getFont().getSize(), Math.max(0, ((int) (y - prefHeight / 2)) + .5));\n\n        label.setText(time.format(getSkinnable().getTimeFormatter()));\n        label.resizeRelocate(snapPositionX(contentX), snapPositionY(y), snapSizeX(contentWidth), snapSizeY(prefHeight));\n    }\n\n    @Override\n    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {\n        double width = 0;\n\n        for (Label label : timeLabels) {\n            width = Math.max(width, label.prefWidth(-1));\n        }\n\n        return width + leftInset + rightInset;\n    }\n\n    @Override\n    protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {\n        return computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayHeaderViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.WeekDayHeaderView;\nimport com.calendarfx.view.WeekDayHeaderView.WeekDayHeaderCell;\nimport javafx.beans.InvalidationListener;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.util.Callback;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static java.time.temporal.ChronoField.DAY_OF_WEEK;\n\npublic class WeekDayHeaderViewSkin extends SkinBase<WeekDayHeaderView> {\n\n    private static final String TODAY = \"today\";\n\n    private final HBox hbox;\n\n    public WeekDayHeaderViewSkin(WeekDayHeaderView view) {\n        super(view);\n\n        hbox = new HBox();\n        hbox.getStyleClass().add(\"container\");\n\n        getChildren().add(hbox);\n\n        final InvalidationListener updateListener = it -> updateControl();\n\n        view.numberOfDaysProperty().addListener(updateListener);\n        view.showTodayProperty().addListener(updateListener);\n        view.weekFieldsProperty().addListener(updateListener);\n        view.enableHyperlinksProperty().addListener(updateListener);\n        view.adjustToFirstDayOfWeekProperty().addListener(updateListener);\n\n        updateControl();\n    }\n\n    private final List<InvalidationListener> listeners = new ArrayList<>();\n\n    private void updateControl() {\n        hbox.getChildren().clear();\n        // the day views\n        WeekDayHeaderView view = getSkinnable();\n        listeners.forEach(l -> view.dateProperty().removeListener(l));\n\n        final int numberOfDays = view.getNumberOfDays();\n\n        Callback<WeekDayHeaderView, Region> separatorFactory = view.getSeparatorFactory();\n        Callback<WeekDayHeaderView, WeekDayHeaderCell> cellFactory = view.getCellFactory();\n\n        for (int i = 0; i < numberOfDays; i++) {\n            WeekDayHeaderCell cell = cellFactory.call(view);\n            cell.setPrefWidth(1); // equal width distribution\n\n            final int dayCount = i;\n\n            InvalidationListener invalidationListener = evt -> updateCell(cell, dayCount);\n            listeners.add(invalidationListener);\n\n            view.dateProperty().addListener(invalidationListener);\n            updateCell(cell, dayCount);\n\n            HBox.setHgrow(cell, Priority.ALWAYS);\n            hbox.getChildren().add(cell);\n\n            if (separatorFactory != null && i < numberOfDays - 1) {\n                Region separator = separatorFactory.call(view);\n                if (separator != null) {\n                    HBox.setHgrow(separator, Priority.NEVER);\n                    hbox.getChildren().add(separator);\n                }\n            }\n        }\n    }\n\n    private void updateCell(WeekDayHeaderCell cell, int dayCount) {\n        LocalDate startDate = getSkinnable().getDate();\n        LocalDate date = getDate(startDate, dayCount);\n        cell.setDate(date);\n\n        if (getSkinnable().isShowToday() && date.equals(getSkinnable().getToday())) {\n            if (!cell.getStyleClass().contains(TODAY)) {\n                cell.getStyleClass().add(TODAY);\n            }\n        } else {\n            cell.getStyleClass().remove(TODAY);\n        }\n    }\n\n    private LocalDate getDate(LocalDate startDate, int dayCount) {\n        if (getSkinnable().isAdjustToFirstDayOfWeek()) {\n            LocalDate newStartDate = startDate.with(DAY_OF_WEEK, getSkinnable().getFirstDayOfWeek().getValue());\n            if (newStartDate.isAfter(startDate)) {\n                startDate = newStartDate.minusWeeks(1);\n            } else {\n                startDate = newStartDate;\n            }\n        }\n\n        return startDate.plusDays(dayCount);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.WeekDayView;\nimport com.calendarfx.view.WeekView;\n\nimport java.time.LocalDate;\n\npublic class WeekDayViewSkin extends DayViewSkin<WeekDayView> {\n\n    public WeekDayViewSkin(WeekDayView view) {\n        super(view);\n    }\n\n    @Override\n    protected boolean isShowingTimeMarker() {\n        WeekDayView dayView = getSkinnable();\n        WeekView weekView = dayView.getWeekView();\n\n        if (weekView != null) {\n            LocalDate today = getSkinnable().getToday();\n\n            LocalDate weekStart = weekView.getStartDate();\n            LocalDate weekEnd = weekView.getEndDate();\n\n            return !(weekStart.isAfter(today) || weekEnd.isBefore(today));\n\n        }\n\n        return false;\n    }\n\n\n    @Override\n    protected boolean isShowingTimeTodayMarker() {\n        WeekDayView dayView = getSkinnable();\n        return dayView.getDate().equals(dayView.getToday());\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekFieldsViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.WeekFieldsView;\nimport javafx.beans.InvalidationListener;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.GridPane;\n\nimport java.time.DayOfWeek;\nimport java.time.temporal.WeekFields;\n\npublic class WeekFieldsViewSkin extends SkinBase<WeekFieldsView> {\n\n    private final ComboBox<DayOfWeek> dayOfWeekComboBox;\n    private final ComboBox<Integer> minimalDaysInFirstWeekComboBox;\n\n    public WeekFieldsViewSkin(final WeekFieldsView view) {\n        super(view);\n\n        dayOfWeekComboBox = new ComboBox<>();\n        dayOfWeekComboBox.getItems().addAll(DayOfWeek.values());\n        dayOfWeekComboBox.setValue(view.getFirstDayOfWeek());\n\n        minimalDaysInFirstWeekComboBox = new ComboBox<>();\n        minimalDaysInFirstWeekComboBox.getItems().addAll(1, 2, 3, 4, 5, 6, 7);\n        minimalDaysInFirstWeekComboBox.setValue(view.getMinimalDaysInFirstWeek());\n\n        GridPane pane = new GridPane();\n        pane.getStyleClass().add(\"content\");\n        pane.add(new Label(\"First day:\"), 0, 0);\n        pane.add(new Label(\"Minimum days:\"), 0, 1);\n        pane.add(dayOfWeekComboBox, 1, 0);\n        pane.add(minimalDaysInFirstWeekComboBox, 1, 1);\n\n        getChildren().add(pane);\n\n        // listeners\n\n        InvalidationListener updateListener = it -> updateValues();\n        dayOfWeekComboBox.valueProperty().addListener(updateListener);\n        minimalDaysInFirstWeekComboBox.valueProperty().addListener(updateListener);\n\n        view.weekFieldsProperty().addListener(it -> {\n            WeekFields fields = view.getWeekFields();\n            dayOfWeekComboBox.setValue(fields.getFirstDayOfWeek());\n            minimalDaysInFirstWeekComboBox.setValue(fields.getMinimalDaysInFirstWeek());\n        });\n    }\n\n    private void updateValues() {\n        DayOfWeek dayOfWeek = dayOfWeekComboBox.getValue();\n        Integer minimumNumberOfDays = minimalDaysInFirstWeekComboBox.getValue();\n        getSkinnable().setWeekFields(WeekFields.of(dayOfWeek, minimumNumberOfDays));\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekTimeScaleViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.WeekTimeScaleView;\n\nimport java.time.LocalDate;\n\n@SuppressWarnings(\"javadoc\")\npublic class WeekTimeScaleViewSkin extends TimeScaleViewSkin<WeekTimeScaleView> {\n\n    public WeekTimeScaleViewSkin(WeekTimeScaleView scale) {\n        super(scale);\n    }\n\n    @Override\n    protected boolean isShowingTimeMarker() {\n        WeekTimeScaleView dayView = getSkinnable();\n        DetailedWeekView weekView = dayView.getDetailedWeekView();\n\n        if (weekView != null) {\n            LocalDate today = getSkinnable().getToday();\n\n            LocalDate weekStart = weekView.getStartDate();\n            LocalDate weekEnd = weekView.getEndDate();\n\n            return !(weekStart.isAfter(today) || weekEnd.isBefore(today));\n\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.WeekDayView;\nimport com.calendarfx.view.WeekView;\nimport com.calendarfx.view.WeekView.WeekDayParameter;\nimport javafx.beans.InvalidationListener;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.shape.Rectangle;\nimport javafx.util.Callback;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.util.List;\n\nimport static java.time.temporal.ChronoField.DAY_OF_WEEK;\n\npublic class WeekViewSkin extends SkinBase<WeekView> {\n\n    private final HBox container = new HBox();\n\n    public WeekViewSkin(WeekView view) {\n        super(view);\n\n\n        final InvalidationListener rebuildListener = it -> buildDays();\n        view.numberOfDaysProperty().addListener(rebuildListener);\n        view.adjustToFirstDayOfWeekProperty().addListener(rebuildListener);\n        view.weekDayViewFactoryProperty().addListener(rebuildListener);\n\n        buildDays();\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(view.widthProperty());\n        clip.heightProperty().bind(view.heightProperty());\n        view.setClip(clip);\n\n        new DayViewEditController(view);\n\n        getChildren().add(container);\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        final double height = container.prefHeight(-1);\n        container.resizeRelocate(contentX, contentY, contentWidth, height);\n    }\n\n    private void buildDays() {\n        WeekView weekView = getSkinnable();\n        List<WeekDayView> weekDayViews = weekView.getWeekDayViews();\n\n        // before rebuilding make sure to unbind previous day view children\n        weekDayViews.forEach(view -> getSkinnable().unbind(view));\n\n        container.getChildren().clear();\n\n        weekDayViews.clear();\n\n        Callback<WeekDayParameter, WeekDayView> weekDayViewFactory = weekView.getWeekDayViewFactory();\n\n        int numberOfDays = weekView.getNumberOfDays();\n\n        Callback<WeekView, Region> separatorFactory = weekView.getSeparatorFactory();\n\n        for (int i = 0; i < numberOfDays; i++) {\n            WeekDayParameter param = new WeekDayParameter(weekView);\n\n            WeekDayView weekDayView = weekDayViewFactory.call(param);\n            weekDayView.setPrefWidth(1); // equal width distribution\n            weekDayView.getProperties().put(\"week.view\", weekView);\n\n            InvalidationListener updateUsedTimesListener = it -> updateUsedTimes();\n            weekDayView.earliestTimeUsedProperty().addListener(updateUsedTimesListener);\n            weekDayView.latestTimeUsedProperty().addListener(updateUsedTimesListener);\n\n            weekDayView.showTodayProperty().bindBidirectional(weekView.showTodayProperty());\n\n            if (i == 0) {\n                weekDayView.getStyleClass().add(\"first-day\");\n            } else if (i == numberOfDays - 1) {\n                weekDayView.getStyleClass().add(\"last-day\");\n            }\n\n            final int dayCount = i;\n\n            final InvalidationListener updateListener = it -> updateDate(weekDayView, dayCount);\n            weekView.dateProperty().addListener(updateListener);\n            weekView.weekFieldsProperty().addListener(updateListener);\n\n            updateDate(weekDayView, dayCount);\n\n            getSkinnable().bind(weekDayView, false);\n\n            HBox.setHgrow(weekDayView, Priority.ALWAYS);\n            container.getChildren().add(weekDayView);\n\n            if (separatorFactory != null && i < numberOfDays - 1) {\n                Region separator = separatorFactory.call(weekView);\n                if (separator != null) {\n                    container.getChildren().add(separator);\n                }\n            }\n\n            weekDayViews.add(weekDayView);\n        }\n    }\n\n    private void updateUsedTimes() {\n        LocalTime earliestTime = null;\n        LocalTime latestTime = null;\n\n        for (WeekDayView view : getSkinnable().getWeekDayViews()) {\n\n            LocalTime etu = view.getEarliestTimeUsed();\n            LocalTime ltu = view.getLatestTimeUsed();\n\n            if (earliestTime == null || (etu != null && etu.isBefore(earliestTime))) {\n                earliestTime = etu;\n            }\n\n            if (latestTime == null || (ltu != null && ltu.isAfter(latestTime))) {\n                latestTime = ltu;\n            }\n        }\n\n        getSkinnable().getProperties().put(\"earliest.time.used\", earliestTime);\n        getSkinnable().getProperties().put(\"latest.time.used\", latestTime);\n    }\n\n    private void updateDate(WeekDayView view, int dayCount) {\n        updateDate(view, getSkinnable().getDate(), dayCount);\n    }\n\n    private void updateDate(WeekDayView view, LocalDate startDate, int dayCount) {\n        LocalDate date = getDate(startDate, dayCount);\n        view.setDate(date);\n    }\n\n    private LocalDate getDate(LocalDate startDate, int dayCount) {\n        if (getSkinnable().isAdjustToFirstDayOfWeek()) {\n            LocalDate newStartDate = startDate.with(DAY_OF_WEEK, getSkinnable().getFirstDayOfWeek().getValue());\n            if (newStartDate.isAfter(startDate)) {\n                startDate = newStartDate.minusWeeks(1);\n            } else {\n                startDate = newStartDate;\n            }\n        }\n\n        return startDate.plusDays(dayCount);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/YearMonthViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarEvent;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.DateControl.DateDetailsParameter;\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.RequestEvent;\nimport com.calendarfx.view.YearMonthView;\nimport impl.com.calendarfx.view.util.Util;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.geometry.HPos;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Control;\nimport javafx.scene.control.Label;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.util.Callback;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport static java.lang.Double.MAX_VALUE;\nimport static java.time.format.TextStyle.SHORT;\nimport static javafx.geometry.Pos.CENTER;\nimport static javafx.scene.control.SelectionMode.SINGLE;\nimport static javafx.scene.layout.Priority.ALWAYS;\n\npublic class YearMonthViewSkin extends DateControlSkin<YearMonthView> implements LoadDataSettingsProvider {\n\n    private static final String DAY_OF_WEEK_LABEL = \"day-of-week-label\";\n    private static final String CURRENT_DATE_LABEL = \"current-date-label\";\n    private static final String CURRENT_DATE_BORDER = \"current-date-border\";\n    private static final String USAGE_VERY_LOW = \"usage-very-low\";\n    private static final String USAGE_LOW = \"usage-low\";\n    private static final String USAGE_MEDIUM = \"usage-medium\";\n    private static final String USAGE_HIGH = \"usage-high\";\n    private static final String USAGE_VERY_HIGH = \"usage-very-high\";\n    private static final String TODAY = \"today\";\n    private static final String DAY_OF_MONTH_LABEL = \"day-of-month-label\";\n    private static final String DAY_NOT_OF_MONTH_LABEL = \"day-not-of-month-label\";\n    private static final String WEEKEND_DAY = \"weekend-day\";\n    private static final String SELECTED_MONTH_DATE = \"selected-month-date\";\n\n    private final GridPane gridPane;\n\n    private final Label monthLabel;\n\n    private final Label yearLabel;\n\n    private final Map<String, YearMonthView.DateCell> cellsMap = new HashMap<>();\n\n    private final Label[] dayOfWeekLabels = new Label[7];\n\n    private final Label[] weekNumberLabels = new Label[6];\n\n    private final DataLoader dataLoader;\n\n    private YearMonth displayedYearMonth;\n\n    public YearMonthViewSkin(YearMonthView view) {\n        super(view);\n\n        dataLoader = new DataLoader(this);\n\n        gridPane = new GridPane();\n        gridPane.setAlignment(Pos.CENTER);\n        gridPane.setMaxSize(MAX_VALUE, MAX_VALUE);\n        gridPane.getStyleClass().add(\"container\");\n\n        monthLabel = new Label();\n        monthLabel.getStyleClass().add(\"month-label\");\n        monthLabel.visibleProperty().bind(view.showMonthProperty());\n\n        yearLabel = new Label();\n        yearLabel.getStyleClass().add(\"year-label\");\n        yearLabel.visibleProperty().bind(view.showYearProperty());\n\n        final InvalidationListener updateViewListener = evt -> updateView();\n        view.yearMonthProperty().addListener(evt -> {\n            if (displayedYearMonth == null || !displayedYearMonth.equals(view.getYearMonth())) {\n                updateView();\n            }\n        });\n\n        final InvalidationListener buildViewListener = evt -> buildView();\n\n        view.showTodayProperty().addListener(updateViewListener);\n        view.getSelectedDates().addListener(updateViewListener);\n\n        view.showUsageColorsProperty().addListener(it -> updateUsageColors(\"show usage colors flag changed\"));\n\n        view.showWeekNumbersProperty().addListener(buildViewListener);\n        view.showMonthArrowsProperty().addListener(buildViewListener);\n        view.showMonthProperty().addListener(buildViewListener);\n        view.showYearProperty().addListener(buildViewListener);\n        view.cellFactoryProperty().addListener(buildViewListener);\n        view.weekFieldsProperty().addListener(buildViewListener);\n        view.showTodayButtonProperty().addListener(buildViewListener);\n        view.showYearArrowsProperty().addListener(buildViewListener);\n\n        view.getCalendars().addListener((Observable it) -> updateUsageColors(\"list of calendars changed\"));\n\n        view.dateProperty().addListener((observable, oldValue, newValue) -> {\n            if (oldValue.getYear() != newValue.getYear()) {\n                updateUsageColors(\"Year has changed.\");\n            }\n        });\n\n        view.suspendUpdatesProperty().addListener(it -> {\n            if (!view.isSuspendUpdates()) {\n                updateUsageColors(\"suspend update set to false\");\n            }\n        });\n\n        getChildren().add(gridPane);\n\n        updateHyperlinkSupport();\n        view.enableHyperlinksProperty().addListener(it -> updateHyperlinkSupport());\n\n        buildView();\n\n        updateVisibility();\n\n        updateUsageColors(\"initial creation of usage colors\");\n    }\n\n    private void updateHyperlinkSupport() {\n        final YearMonthView view = getSkinnable();\n\n        if (view.isEnableHyperlinks()) {\n            monthLabel.setOnMouseClicked(evt -> {\n                if (evt.getClickCount() == 1) {\n                    view.fireEvent(new RequestEvent(view, view, getSkinnable().getYearMonth()));\n                }\n            });\n\n            yearLabel.setOnMouseClicked(evt -> {\n                if (evt.getClickCount() == 1) {\n                    view.fireEvent(new RequestEvent(view, view, Year.of(getSkinnable().getYearMonth().getYear())));\n                }\n            });\n        } else {\n            monthLabel.setOnMouseClicked(null);\n            yearLabel.setOnMouseClicked(null);\n        }\n    }\n\n    private void updateVisibility() {\n        for (int row = 0; row < 6; row++) {\n            RowConstraints rowConstraints = new RowConstraints();\n            rowConstraints.setFillHeight(true);\n            rowConstraints.setMinHeight(Region.USE_PREF_SIZE);\n            rowConstraints.setMaxHeight(Region.USE_COMPUTED_SIZE);\n            rowConstraints.setPrefHeight(Region.USE_COMPUTED_SIZE);\n            rowConstraints.setVgrow(Priority.ALWAYS);\n            gridPane.getRowConstraints().add(rowConstraints);\n        }\n\n        ColumnConstraints weekColumn = new ColumnConstraints();\n        weekColumn.setHalignment(HPos.RIGHT);\n        weekColumn.setMaxWidth(Region.USE_COMPUTED_SIZE);\n        weekColumn.setMinWidth(Region.USE_PREF_SIZE);\n        weekColumn.setPrefWidth(Region.USE_COMPUTED_SIZE);\n        weekColumn.setFillWidth(true);\n        weekColumn.setHgrow(Priority.NEVER);\n        gridPane.getColumnConstraints().add(weekColumn);\n\n        for (int col = 0; col < 7; col++) {\n            ColumnConstraints columnConstraints = new ColumnConstraints();\n            columnConstraints.setHalignment(HPos.CENTER);\n            columnConstraints.setMaxWidth(Region.USE_COMPUTED_SIZE);\n            columnConstraints.setMinWidth(Region.USE_PREF_SIZE);\n            columnConstraints.setPrefWidth(Region.USE_COMPUTED_SIZE);\n            columnConstraints.setFillWidth(true);\n            columnConstraints.setHgrow(Priority.ALWAYS);\n            gridPane.getColumnConstraints().add(columnConstraints);\n        }\n    }\n\n    @Override\n    protected void refreshData() {\n        updateView();\n    }\n\n    @Override\n    protected void calendarVisibilityChanged() {\n        updateUsageColors(\"calendar visibility changed\");\n    }\n\n    @Override\n    protected void calendarChanged(Calendar calendar) {\n        updateUsageColors(\"changes in calendar \" + calendar.getName());\n    }\n\n    @Override\n    protected void entryCalendarChanged(CalendarEvent evt) {\n        Entry<?> entry = evt.getEntry();\n\n        /*\n         * If an entry has been deleted (new calendar == null) then we have no\n         * way of finding out\n         */\n        if (isRelevant(entry)) {\n            updateUsageColors(\"entry calendar changed\");\n        }\n    }\n\n    @Override\n    protected void entryIntervalChanged(CalendarEvent evt) {\n        if (evt.isDayChange()) {\n            Entry<?> entry = evt.getEntry();\n\n            if (isRelevant(entry) || isRelevant(evt.getOldInterval())) {\n                updateUsageColors(\"entry interval changed\");\n            }\n        }\n    }\n\n    @Override\n    protected void entryFullDayChanged(CalendarEvent evt) {\n    }\n\n    @Override\n    protected void entryRecurrenceRuleChanged(CalendarEvent evt) {\n        Entry<?> entry = evt.getEntry();\n\n        if (isRelevant(entry)) {\n            updateUsageColors(\"entry recurrence rule changed\");\n        }\n    }\n\n    private LocalDate lastSelectedDate;\n\n    private void buildView() {\n        gridPane.getChildren().clear();\n\n        YearMonthView view = getSkinnable();\n\n        BorderPane header = new BorderPane();\n        header.getStyleClass().add(\"header\");\n\n        BorderPane leftHeader = new BorderPane();\n        leftHeader.getStyleClass().add(\"month-header\");\n\n        if (getSkinnable().isShowMonth()) {\n            if (getSkinnable().isShowMonthArrows()) {\n                // left: previous year button\n                Region prevMonthRegion = new Region();\n                BorderPane.setAlignment(prevMonthRegion, Pos.CENTER);\n                BorderPane.setMargin(prevMonthRegion, new Insets(0, 6, 0, 6));\n                prevMonthRegion.getStyleClass().add(\"previous-button\");\n                leftHeader.setLeft(prevMonthRegion);\n                prevMonthRegion.setOnMouseClicked(evt -> getSkinnable().setDate(getSkinnable().getDate().minusMonths(1)));\n            }\n\n            // center: year label\n            leftHeader.setCenter(monthLabel);\n\n            if (getSkinnable().isShowMonthArrows()) {\n                // left: next year button\n                Region nextMonthRegion = new Region();\n                BorderPane.setAlignment(nextMonthRegion, Pos.CENTER);\n                BorderPane.setMargin(nextMonthRegion, new Insets(0, 6, 0, 6));\n                nextMonthRegion.getStyleClass().add(\"next-button\");\n                leftHeader.setRight(nextMonthRegion);\n                nextMonthRegion.setOnMouseClicked(evt -> getSkinnable().setDate(getSkinnable().getDate().plusMonths(1)));\n            }\n\n            header.setLeft(leftHeader);\n        }\n\n        if (getSkinnable().isShowYear()) {\n            BorderPane rightHeader = new BorderPane();\n            rightHeader.getStyleClass().add(\"year-header\");\n\n            if (getSkinnable().isShowYearArrows()) {\n                // left: previous year button\n                Region prevYearRegion = new Region();\n                BorderPane.setAlignment(prevYearRegion, Pos.CENTER);\n                BorderPane.setMargin(prevYearRegion, new Insets(0, 6, 0, 6));\n                prevYearRegion.getStyleClass().add(\"previous-button\");\n                rightHeader.setLeft(prevYearRegion);\n                prevYearRegion.setOnMouseClicked(evt -> getSkinnable().setDate(getSkinnable().getDate().minusYears(1)));\n            }\n\n            // center: year label\n            rightHeader.setCenter(yearLabel);\n\n            if (getSkinnable().isShowYearArrows()) {\n                // left: next year button\n                Region nextYearRegion = new Region();\n                BorderPane.setAlignment(nextYearRegion, Pos.CENTER);\n                BorderPane.setMargin(nextYearRegion, new Insets(0, 6, 0, 6));\n                nextYearRegion.getStyleClass().add(\"next-button\");\n                rightHeader.setRight(nextYearRegion);\n                nextYearRegion.setOnMouseClicked(evt -> getSkinnable().setDate(getSkinnable().getDate().plusYears(1)));\n            }\n\n            header.setRight(rightHeader);\n        }\n\n        GridPane.setColumnSpan(header, 7);\n\n        gridPane.add(header, 1, 0);\n\n        DayOfWeek dayOfWeek = view.getFirstDayOfWeek();\n        for (int i = 0; i < 7; i++) {\n            dayOfWeekLabels[i] = new Label(dayOfWeek.getDisplayName(SHORT, Locale.getDefault()));\n            dayOfWeekLabels[i].setAlignment(CENTER);\n            dayOfWeekLabels[i].setMaxSize(MAX_VALUE, MAX_VALUE);\n            dayOfWeekLabels[i].getStyleClass().add(DAY_OF_WEEK_LABEL);\n            gridPane.add(dayOfWeekLabels[i], i + 1, 1);\n            dayOfWeek = dayOfWeek.plus(1);\n        }\n\n        final DayOfWeek firstDayOfWeek = getSkinnable().getFirstDayOfWeek();\n\n        LocalDate date = getLoadStartDate();\n        date = Util.adjustToFirstDayOfWeek(date, firstDayOfWeek);\n\n        if (getSkinnable().isShowWeekNumbers()) {\n            for (int i = 0; i < 6; i++) {\n                weekNumberLabels[i] = new Label();\n                weekNumberLabels[i].setMaxSize(MAX_VALUE, MAX_VALUE);\n                weekNumberLabels[i].setAlignment(Pos.CENTER_RIGHT);\n                weekNumberLabels[i].getStyleClass().add(\"week-label\");\n                gridPane.add(weekNumberLabels[i], 0, 2 + i);\n                date = date.plusWeeks(1);\n            }\n        }\n\n        for (int row = 0; row < 6; row++) {\n            for (int col = 0; col < 7; col++) {\n                Callback<YearMonthView, YearMonthView.DateCell> cellFactory = view.getCellFactory();\n                YearMonthView.DateCell cell = cellFactory.call(getSkinnable());\n                GridPane.setHgrow(cell, ALWAYS);\n                GridPane.setVgrow(cell, ALWAYS);\n                cell.addEventHandler(MouseEvent.MOUSE_CLICKED, evt -> handleMouseClick(evt, cell, cell.getDate()));\n                cell.getStyleClass().add(\"month-day\");\n                cellsMap.put(getKey(row, col), cell);\n                gridPane.add(cell, col + 1, row + 2);\n                date = date.plusDays(1);\n            }\n        }\n\n        if (getSkinnable().isShowTodayButton()) {\n            Button button = new Button(Messages.getString(\"YearMonthViewSkin.TODAY\"));\n            gridPane.add(button, 0, 9);\n            GridPane.setColumnSpan(button, 8);\n            GridPane.setHalignment(button, HPos.CENTER);\n            GridPane.setMargin(button, new Insets(6, 0, 0, 0));\n            button.setOnAction(evt -> getSkinnable().setDate(getSkinnable().getToday()));\n        }\n\n        // after a build we always have to update the view\n        updateView();\n    }\n\n    private String getKey(int row, int col) {\n        return row + \"/\" + col;\n    }\n\n    private void updateView() {\n        lastSelectedDate = null;\n\n        YearMonthView view = getSkinnable();\n        YearMonth yearMonth = view.getYearMonth();\n\n        displayedYearMonth = yearMonth;\n\n        boolean currentYearMonth = getSkinnable().getYearMonth().equals(YearMonth.from(getSkinnable().getToday()));\n\n        monthLabel.getStyleClass().remove(CURRENT_DATE_LABEL);\n        yearLabel.getStyleClass().remove(CURRENT_DATE_LABEL);\n\n        if (currentYearMonth && view.isShowToday()) {\n            monthLabel.getStyleClass().add(CURRENT_DATE_LABEL);\n            yearLabel.getStyleClass().add(CURRENT_DATE_LABEL);\n        }\n\n        monthLabel.setText(DateTimeFormatter.ofPattern(Messages.getString(\"YearMonthViewSkin.MONTH_FORMAT\")).format(yearMonth));\n        yearLabel.setText(DateTimeFormatter.ofPattern(Messages.getString(\"YearMonthViewSkin.YEAR_FORMAT\")).format(yearMonth));\n\n        // update the week days (mon, tues, wed, ....)\n\n        DayOfWeek dayOfWeek = view.getFirstDayOfWeek();\n        for (int i = 0; i < 7; i++) {\n            if (view.isShowToday()) {\n                if (currentYearMonth) {\n                    dayOfWeekLabels[i].getStyleClass().add(CURRENT_DATE_BORDER);\n                } else {\n                    dayOfWeekLabels[i].getStyleClass().removeAll(CURRENT_DATE_BORDER);\n                }\n\n                if (currentYearMonth && view.getToday().getDayOfWeek().equals(dayOfWeek)) {\n                    dayOfWeekLabels[i].getStyleClass().add(CURRENT_DATE_LABEL);\n                }\n            }\n        }\n\n        // update the days (1 to 31) plus padding days\n\n        final DayOfWeek firstDayOfWeek = getSkinnable().getFirstDayOfWeek();\n        LocalDate date = Util.adjustToFirstDayOfWeek(getLoadStartDate(), firstDayOfWeek);\n\n        if (getSkinnable().isShowWeekNumbers()) {\n            for (int i = 0; i < 6; i++) {\n                int weekOfYear = date.get(getSkinnable().getWeekFields().weekOfYear());\n                weekNumberLabels[i].setText(Integer.toString(weekOfYear));\n                date = date.plusWeeks(1);\n            }\n        }\n\n        date = Util.adjustToFirstDayOfWeek(getLoadStartDate(), firstDayOfWeek);\n\n        for (int row = 0; row < 6; row++) {\n            for (int col = 0; col < 7; col++) {\n                LocalDate localDate = LocalDate.from(date);\n\n                YearMonthView.DateCell cell = cellsMap.get(getKey(row, col));\n                cell.setDate(localDate);\n                cell.getStyleClass().removeAll(TODAY, DAY_OF_MONTH_LABEL, DAY_NOT_OF_MONTH_LABEL, WEEKEND_DAY, SELECTED_MONTH_DATE);\n\n                if (getSkinnable().getSelectedDates().contains(date)) {\n                    cell.getStyleClass().add(SELECTED_MONTH_DATE); // $NON-NLS-1$\n                }\n\n                if (YearMonth.from(date).equals(YearMonth.from(getSkinnable().getDate()))) {\n                    if (getSkinnable().isShowToday() && date.equals(getSkinnable().getToday())) {\n                        cell.getStyleClass().addAll(DAY_OF_MONTH_LABEL, TODAY);\n                    } else {\n                        cell.getStyleClass().add(DAY_OF_MONTH_LABEL); // $NON-NLS-1$\n                    }\n                } else {\n                    cell.getStyleClass().add(DAY_NOT_OF_MONTH_LABEL); // $NON-NLS-1$\n                }\n\n                if (view.getWeekendDays().contains(date.getDayOfWeek())) {\n                    cell.getStyleClass().add(WEEKEND_DAY); // $NON-NLS-1$\n                }\n\n                date = date.plusDays(1);\n            }\n        }\n    }\n\n    private void handleMouseClick(MouseEvent evt, Node node, LocalDate date) {\n        switch (evt.getClickCount()) {\n        case 1:\n            handleSingleClick(evt, node, date);\n            break;\n        case 2:\n            if (getSkinnable().isEnableHyperlinks()) {\n                handleDoubleClick(date);\n            }\n            break;\n        default:\n            break;\n        }\n    }\n\n    private void handleSingleClick(MouseEvent evt, Node node, LocalDate date) {\n        if (!(evt.getButton() == MouseButton.PRIMARY)) {\n            return;\n        }\n\n        YearMonthView view = getSkinnable();\n        switch (view.getClickBehaviour()) {\n        case NONE:\n            break;\n        case SHOW_DETAILS:\n            Callback<DateDetailsParameter, Boolean> callback = view.getDateDetailsCallback();\n            if (callback != null) {\n                callback.call(new DateDetailsParameter(evt, view, node, node.getScene().getRoot(), date, evt.getScreenX(), evt.getScreenY()));\n            }\n            break;\n        case PERFORM_SELECTION:\n            boolean multiSelect = evt.isShiftDown() || evt.isShortcutDown();\n            if (!multiSelect || (view.getSelectionMode().equals(SINGLE)\n                    && !evt.isControlDown())) {\n                view.getSelectedDates().clear();\n            }\n\n            if (evt.isShiftDown()) {\n                if (lastSelectedDate != null) {\n                    LocalDate st = lastSelectedDate;\n                    LocalDate et = date;\n                    if (date.isBefore(st)) {\n                        st = date;\n                        et = lastSelectedDate;\n                    }\n\n                    do {\n                        view.getSelectedDates().add(st);\n                        st = st.plusDays(1);\n                    } while (!et.isBefore(st));\n                } else {\n                    view.getSelectedDates().clear();\n                    view.getSelectedDates().add(date);\n                }\n            } else {\n                if (view.getSelectedDates().contains(date)) {\n                    view.getSelectedDates().remove(date);\n                } else {\n                    view.getSelectedDates().add(date);\n                }\n            }\n\n            lastSelectedDate = date;\n\n            if (!date.getMonth().equals(view.getYearMonth().getMonth())) {\n                view.setDate(date);\n            }\n\n            break;\n        default:\n            break;\n        }\n    }\n\n    private void handleDoubleClick(LocalDate date) {\n        YearMonthView view = getSkinnable();\n        view.fireEvent(new RequestEvent(view, view, date));\n    }\n\n    private void updateUsageColors(String reason) {\n        cellsMap.values()\n                .forEach(control -> control.getStyleClass().removeAll(\n                        USAGE_VERY_LOW, USAGE_LOW, USAGE_MEDIUM, USAGE_HIGH,\n                        USAGE_VERY_HIGH));\n\n        if (!getSkinnable().isShowUsageColors()) {\n            return;\n        }\n\n        LoggingDomain.VIEW.fine(\"updating colors: reason = \" + reason\n                + \", year month = \" + getSkinnable().getYearMonth());\n\n        Map<LocalDate, List<Entry<?>>> dataMap = new HashMap<>();\n        dataLoader.loadEntries(dataMap);\n\n        for (String key : cellsMap.keySet()) {\n            YearMonthView.DateCell cell = cellsMap.get(key);\n            LocalDate date = cell.getDate();\n\n            if (date.isEqual(getSkinnable().getToday())\n                    && getSkinnable().isShowToday()) {\n                continue;\n            }\n\n            int entryCount = 0;\n            List<Entry<?>> entries = dataMap.get(date);\n            if (entries != null) {\n                entryCount = entries.size();\n            }\n\n            final Callback<Integer, DateControl.Usage> usagePolicy = getSkinnable()\n                    .getUsagePolicy();\n\n            switch (usagePolicy.call(entryCount)) {\n            case NONE:\n                break;\n            case VERY_LOW:\n                cell.getStyleClass().add(USAGE_VERY_LOW);\n                break;\n            case LOW:\n                cell.getStyleClass().add(USAGE_LOW);\n                break;\n            case MEDIUM:\n                cell.getStyleClass().add(USAGE_MEDIUM);\n                break;\n            case HIGH:\n                cell.getStyleClass().add(USAGE_HIGH);\n                break;\n            case VERY_HIGH:\n            default:\n                cell.getStyleClass().add(USAGE_VERY_HIGH);\n                break;\n            }\n        }\n    }\n\n    @Override\n    public String getLoaderName() {\n        return \"Year Month View\";\n    }\n\n    @Override\n    public LocalDate getLoadStartDate() {\n        /*\n         * The month view also shows the last couple of days of the previous\n         * month.\n         */\n        return Util.adjustToFirstDayOfWeek(\n                getSkinnable().getDate().withDayOfMonth(1),\n                getSkinnable().getFirstDayOfWeek());\n    }\n\n    @Override\n    public LocalDate getLoadEndDate() {\n        /*\n         * The month view also shows the first couple of days of the next month.\n         */\n        return getLoadStartDate().plusDays(41); // the view always shows 41\n                                                // month days\n    }\n\n    @Override\n    public ZoneId getZoneId() {\n        return getSkinnable().getZoneId();\n    }\n\n    @Override\n    public List<CalendarSource> getCalendarSources() {\n        YearMonthView view = getSkinnable();\n        return view.getCalendarSources();\n    }\n\n    @Override\n    public Control getControl() {\n        return getSkinnable();\n    }\n\n    @Override\n    public boolean isCalendarVisible(Calendar calendar) {\n        return getSkinnable().isCalendarVisible(calendar);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/YearViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view;\n\nimport com.calendarfx.view.YearMonthView;\nimport com.calendarfx.view.YearView;\nimport javafx.geometry.HPos;\nimport javafx.geometry.VPos;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\n\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.time.YearMonth;\nimport static java.lang.Double.MAX_VALUE;\n\npublic class YearViewSkin extends DateControlSkin<YearView> {\n\n    public YearViewSkin(YearView view) {\n        super(view);\n\n        view.dateProperty().addListener(evt -> updateMonths());\n\n        ScrollPane scrollPane = new ScrollPane();\n        \n        GridPane gridPane = new GridPane();\n        gridPane.getStyleClass().add(\"container\");\n        gridPane.setMaxSize(MAX_VALUE, MAX_VALUE);\n\n        for (int row = 0; row < 3; row++) {\n            RowConstraints rowConstraints = new RowConstraints();\n            rowConstraints.setMinHeight(Region.USE_PREF_SIZE);\n            rowConstraints.setPrefHeight(Region.USE_COMPUTED_SIZE);\n            rowConstraints.setMaxHeight(Region.USE_COMPUTED_SIZE);\n            rowConstraints.setVgrow(Priority.ALWAYS);\n            rowConstraints.setValignment(VPos.CENTER);\n            gridPane.getRowConstraints().add(rowConstraints);\n        }\n\n        for (int col = 0; col < 4; col++) {\n            ColumnConstraints colConstraints = new ColumnConstraints();\n            colConstraints.setMinWidth(Region.USE_PREF_SIZE);\n            colConstraints.setPrefWidth(Region.USE_COMPUTED_SIZE);\n            colConstraints.setMaxWidth(Region.USE_COMPUTED_SIZE);\n            colConstraints.setHgrow(Priority.ALWAYS);\n            colConstraints.setHalignment(HPos.CENTER);\n            gridPane.getColumnConstraints().add(colConstraints);\n        }\n\n        for (int row = 0; row < 3; row++) {\n            for (int col = 0; col < 4; col++) {\n                Month month = Month.of(row * 4 + col + 1);\n\n                YearMonthView yearMonthView = view.getMonthView(month);\n                yearMonthView.setShowMonthArrows(false);\n                yearMonthView.setShowTodayButton(false);\n                yearMonthView.setShowUsageColors(true);\n                yearMonthView.setClickBehaviour(YearMonthView.ClickBehaviour.SHOW_DETAILS);\n                gridPane.add(yearMonthView, col, row);\n\n                // do not bind date, we manage it manually\n                view.bind(yearMonthView, false);\n            }\n        }\n\n        scrollPane.setContent(gridPane);\n        scrollPane.setFitToHeight(true);\n        scrollPane.setFitToWidth(true);\n        getChildren().add(scrollPane);\n\n        updateMonths();\n    }\n\n    private void updateMonths() {\n        YearView yearPage = getSkinnable();\n        LocalDate date = yearPage.getDate();\n        int year = date.getYear();\n\n        for (Month month : Month.values()) {\n            YearMonth yearMonth = YearMonth.of(year, month);\n            YearMonthView view = yearPage.getMonthView(month);\n            view.setMinSize(0, 0);\n            view.setDate(yearMonth.atDay(1));\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/ZoneIdStringConverter.java",
    "content": "package impl.com.calendarfx.view;\n\nimport javafx.util.StringConverter;\n\nimport java.time.ZoneId;\nimport java.time.format.TextStyle;\nimport java.util.Locale;\n\npublic class ZoneIdStringConverter extends StringConverter<ZoneId> {\n\n    public ZoneIdStringConverter() {\n    }\n\n    @Override\n    public String toString(ZoneId id) {\n        if (id != null) {\n            return id.getDisplayName(TextStyle.FULL_STANDALONE, Locale.getDefault()) + \" (\" + id.getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault()) + \")\";\n        }\n        return \"\";\n    }\n\n    @Override\n    public ZoneId fromString(String id) {\n        return ZoneId.of(id);\n    }\n}"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/page/DayPageSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.page;\n\nimport com.calendarfx.view.AgendaView;\nimport com.calendarfx.view.AllDayView;\nimport com.calendarfx.view.DetailedDayView;\nimport com.calendarfx.view.YearMonthView;\nimport com.calendarfx.view.page.DayPage;\nimport javafx.beans.Observable;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.OverrunStyle;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\n\nimport java.time.LocalDate;\n\npublic class DayPageSkin extends PageBaseSkin<DayPage> {\n\n    private Label todayLabel;\n\n    private YearMonthView yearMonthView;\n\n    private Node leftSide;\n\n    private Node rightSide;\n\n    private ColumnConstraints leftColumn;\n\n    private ColumnConstraints rightColumn;\n\n    private GridPane gridPane;\n\n    public DayPageSkin(DayPage view) {\n        super(view);\n\n        yearMonthView.getSelectedDates().addListener((Observable evt) -> {\n            if (yearMonthView.getSelectedDates().size() == 1) {\n                LocalDate date = yearMonthView.getSelectedDates().iterator().next();\n                getSkinnable().setDate(date);\n            }\n        });\n\n        updateView();\n\n        view.dateProperty().addListener(evt -> updateView());\n\n        view.dayPageLayoutProperty().addListener(it -> updateLayout());\n        updateLayout();\n    }\n\n    private void updateView() {\n        LocalDate date = getSkinnable().getDate();\n        todayLabel.setText(Long.toString(date.getDayOfMonth()));\n\n        yearMonthView.getSelectedDates().clear();\n        yearMonthView.getSelectedDates().add(date);\n    }\n\n    @Override\n    protected Node createContent() {\n        leftSide = createLeftHandSide();\n        rightSide = createRightHandSide();\n\n        leftColumn = new ColumnConstraints();\n        leftColumn.setPercentWidth(50);\n        leftColumn.setMinWidth(Region.USE_COMPUTED_SIZE);\n        leftColumn.setPrefWidth(Region.USE_COMPUTED_SIZE);\n        leftColumn.setMaxWidth(Double.MAX_VALUE);\n        leftColumn.setFillWidth(true);\n\n        rightColumn = new ColumnConstraints();\n        rightColumn.setPercentWidth(50);\n        rightColumn.setMinWidth(Region.USE_COMPUTED_SIZE);\n        rightColumn.setPrefWidth(Region.USE_COMPUTED_SIZE);\n        rightColumn.setMaxWidth(Double.MAX_VALUE);\n        rightColumn.setFillWidth(true);\n\n        RowConstraints rowConstraints = new RowConstraints();\n        rowConstraints.setPercentHeight(100);\n        rowConstraints.setFillHeight(true);\n\n        // no need to assign a style class, will be auto-assigned by superclass (\"content\")\n        gridPane = new GridPane();\n        gridPane.setHgap(20);\n        gridPane.getColumnConstraints().addAll(leftColumn, rightColumn);\n        gridPane.getRowConstraints().addAll(rowConstraints);\n\n        gridPane.add(leftSide, 0, 0);\n        gridPane.add(rightSide, 1, 0);\n\n        getSkinnable().widthProperty().addListener(it -> updateLayout());\n\n        return gridPane;\n    }\n\n    private void updateLayout() {\n        final DayPage page = getSkinnable();\n\n        switch (page.getDayPageLayout()) {\n            case STANDARD:\n                final Insets insets = page.getInsets();\n                if (page.getWidth() - insets.getLeft() - insets.getRight() < leftSide.prefWidth(-1) + rightSide.prefWidth(-1)) {\n                    leftSide.setVisible(false);\n                    rightSide.setVisible(true);\n                    leftColumn.setPercentWidth(0);\n                    rightColumn.setPercentWidth(100);\n                } else {\n                    leftSide.setVisible(true);\n                    rightSide.setVisible(true);\n                    leftColumn.setPercentWidth(50);\n                    rightColumn.setPercentWidth(50);\n                }\n                gridPane.setHgap(20);\n                break;\n            case AGENDA_ONLY:\n                leftSide.setVisible(true);\n                rightSide.setVisible(false);\n                leftColumn.setPercentWidth(100);\n                rightColumn.setPercentWidth(0);\n                gridPane.setHgap(0);\n                break;\n            case DAY_ONLY:\n                leftSide.setVisible(false);\n                rightSide.setVisible(true);\n                leftColumn.setPercentWidth(0);\n                rightColumn.setPercentWidth(100);\n                gridPane.setHgap(0);\n                break;\n        }\n    }\n\n    protected Node createLeftHandSide() {\n        DayPage dayPage = getSkinnable();\n\n        // today label\n        todayLabel = new Label();\n        todayLabel.getStyleClass().add(\"today-label\");\n        todayLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        todayLabel.setAlignment(Pos.TOP_LEFT);\n        todayLabel.setTextOverrun(OverrunStyle.CLIP);\n        todayLabel.setMinWidth(Region.USE_PREF_SIZE);\n        HBox.setHgrow(todayLabel, Priority.ALWAYS);\n\n        // year month view\n        yearMonthView = dayPage.getYearMonthView();\n        yearMonthView.setSelectionMode(SelectionMode.SINGLE);\n        yearMonthView.setShowMonth(false);\n        yearMonthView.setShowYear(false);\n        yearMonthView.setShowTodayButton(false);\n        HBox.setHgrow(yearMonthView, Priority.NEVER);\n        getSkinnable().bind(yearMonthView, true);\n\n        HBox header = new HBox(10);\n        header.setFillHeight(true);\n        header.getChildren().addAll(todayLabel, yearMonthView);\n        header.getStyleClass().add(\"header\");\n\n        AgendaView agendaView = dayPage.getAgendaView();\n        getSkinnable().bind(agendaView, false);\n        agendaView.dateProperty().bind(dayPage.todayProperty());\n\n        HBox.setMargin(agendaView, new Insets(10, 0, 0, 0));\n\n        BorderPane leftHandSide = new BorderPane();\n        leftHandSide.getStyleClass().add(\"left-side\");\n        leftHandSide.setTop(header);\n        leftHandSide.setCenter(agendaView);\n        HBox.setHgrow(leftHandSide, Priority.ALWAYS);\n        return leftHandSide;\n    }\n\n    protected Node createRightHandSide() {\n        DayPage dayPage = getSkinnable();\n\n        // the day view\n        DetailedDayView dayView = dayPage.getDetailedDayView();\n\n        getSkinnable().bind(dayView, true);\n\n        AllDayView allDayView = dayView.getAllDayView();\n        allDayView.showTodayProperty().unbindBidirectional(dayView.showTodayProperty()); // we need control over this\n\n        return dayView;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/page/MonthPageSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.page;\n\nimport com.calendarfx.view.MonthView;\nimport com.calendarfx.view.page.MonthPage;\nimport javafx.scene.Node;\n\npublic class MonthPageSkin extends PageBaseSkin<MonthPage> {\n\n    public MonthPageSkin(MonthPage view) {\n        super(view);\n    }\n\n    @Override\n    protected Node createContent() {\n        MonthPage monthPage = getSkinnable();\n        MonthView monthView = monthPage.getMonthView();\n        monthView.setMinSize(0, 0);\n        monthPage.bind(monthView, true);\n        return monthView;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/page/PageBaseSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.page;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.page.PageBase;\nimport impl.com.calendarfx.view.NavigateDateView;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.text.Text;\n\nimport java.time.format.DateTimeFormatter;\n\npublic abstract class PageBaseSkin<C extends PageBase> extends SkinBase<C> {\n\n    private final Text dateText;\n    private final BorderPane headerPane;\n    private final BorderPane borderPane;\n\n    public PageBaseSkin(C page) {\n        super(page);\n\n        // Navigation\n\n        NavigateDateView navigateDateButton = new NavigateDateView();\n        navigateDateButton.getTodayButton().setText(Messages.getString(\"PageBaseSkin.TODAY\"));\n        navigateDateButton.setOnBackward(() -> page.goBack());\n        navigateDateButton.setOnForward(() -> page.goForward());\n        navigateDateButton.setOnToday(() -> page.goToday());\n\n        navigateDateButton.visibleProperty().bind(page.showNavigationProperty());\n\n        // Date label\n        this.dateText = new Text(\"Date\");\n        this.dateText.getStyleClass().add(\"date-text\");\n        this.dateText.visibleProperty().bind(page.showDateProperty());\n        page.dateProperty().addListener(evt -> updateDateText());\n\n        BorderPane.setMargin(navigateDateButton, new Insets(10));\n        BorderPane.setMargin(dateText, new Insets(10));\n        BorderPane.setAlignment(navigateDateButton, Pos.CENTER_LEFT);\n        BorderPane.setAlignment(dateText, Pos.CENTER_RIGHT);\n\n        headerPane = new BorderPane();\n        headerPane.getStyleClass().add(\"header\");\n        headerPane.setLeft(navigateDateButton);\n        headerPane.setRight(dateText);\n\n        Node content = createContent();\n        content.getStyleClass().add(\"content\");\n        content.sceneProperty().addListener(it -> {\n            if (content.getScene() != null) {\n                content.applyCss();\n            }\n        });\n\n        borderPane = new BorderPane();\n        borderPane.getStyleClass().add(\"container\");\n        borderPane.setCenter(content);\n\n        getChildren().add(borderPane);\n\n        updateDateText();\n        updateHeaderVisibility();\n\n        page.showDateProperty().addListener(it -> updateHeaderVisibility());\n        page.showNavigationProperty().addListener(it -> updateHeaderVisibility());\n    }\n\n    private void updateHeaderVisibility() {\n        if (getSkinnable().isShowDate() || getSkinnable().isShowNavigation()) {\n            borderPane.setTop(headerPane);\n        } else {\n            borderPane.setTop(null);\n        }\n    }\n\n    private void updateDateText() {\n        DateTimeFormatter formatter = getSkinnable().getDateTimeFormatter();\n        String text = formatter.format(getSkinnable().getDate());\n        dateText.setText(text);\n    }\n\n    protected abstract Node createContent();\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/page/WeekPageSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.page;\n\nimport com.calendarfx.view.DetailedWeekView;\nimport com.calendarfx.view.page.WeekPage;\nimport javafx.beans.binding.Bindings;\nimport javafx.scene.Node;\n\n@SuppressWarnings(\"javadoc\")\npublic class WeekPageSkin extends PageBaseSkin<WeekPage> {\n\n    public WeekPageSkin(WeekPage view) {\n        super(view);\n    }\n\n    @Override\n    protected Node createContent() {\n        WeekPage weekPage = getSkinnable();\n        DetailedWeekView detailedWeekView = weekPage.getDetailedWeekView();\n\n        weekPage.bind(detailedWeekView, true);\n\n        Bindings.bindBidirectional(detailedWeekView.startTimeProperty(), weekPage.startTimeProperty());\n        Bindings.bindBidirectional(detailedWeekView.endTimeProperty(), weekPage.endTimeProperty());\n\n        return detailedWeekView;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/page/YearPageSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.page;\n\nimport com.calendarfx.view.MonthSheetView;\nimport com.calendarfx.view.YearView;\nimport com.calendarfx.view.page.YearPage;\nimport javafx.scene.Node;\nimport javafx.scene.input.ScrollEvent;\nimport javafx.scene.layout.StackPane;\n\n@SuppressWarnings(\"javadoc\")\npublic class YearPageSkin extends PageBaseSkin<YearPage> {\n\n    private YearView yearView;\n    private MonthSheetView sheetView;\n    private StackPane stackPane;\n\n    public YearPageSkin(YearPage view) {\n        super(view);\n\n        view.addEventFilter(ScrollEvent.SCROLL, this::handleScroll);\n        view.displayModeProperty().addListener(it -> updateVisibility());\n\n        updateVisibility();\n    }\n\n    private void updateVisibility() {\n        switch (getSkinnable().getDisplayMode()) {\n            case COLUMNS:\n                yearView.setManaged(false);\n                yearView.setVisible(false);\n                sheetView.setManaged(true);\n                sheetView.setVisible(true);\n                if(!stackPane.getChildren().contains(sheetView)) {\n                    stackPane.getChildren().add(sheetView);\n                }\n                break;\n            case GRID:\n                yearView.setManaged(true);\n                yearView.setVisible(true);\n                sheetView.setManaged(false);\n                sheetView.setVisible(false);\n                if(!stackPane.getChildren().contains(yearView)) {\n                    stackPane.getChildren().add(yearView);\n                }\n                break;\n        }\n    }\n\n    private void handleScroll(ScrollEvent evt) {\n        YearPage yearPage = getSkinnable();\n        double delta = evt.getDeltaX();\n        if (delta == 0) {\n            return;\n        }\n        if (delta < 0) {\n            yearPage.goForward();\n        } else if (delta > 0) {\n            yearPage.goBack();\n        }\n    }\n\n    @Override\n    protected Node createContent() {\n        stackPane = new StackPane();\n\n        this.sheetView = getSkinnable().getMonthSheetView();\n        this.sheetView.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        this.yearView = getSkinnable().getYearView();\n        this.yearView.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        return stackPane;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/popover/RecurrencePopupSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.popover;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.popover.RecurrencePopup;\nimport com.calendarfx.view.popover.RecurrencePopup.RecurrencePopupEvent;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Skin;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.StackPane;\n\n@SuppressWarnings(\"javadoc\")\npublic class RecurrencePopupSkin implements Skin<RecurrencePopup> {\n\n    private final StackPane stackPane;\n    private final RecurrencePopup popup;\n\n    public RecurrencePopupSkin(RecurrencePopup popup) {\n        super();\n\n        this.popup = popup;\n\n        Button okButton = new Button(Messages.getString(\"RecurrencePopupSkin.OK\"));\n        okButton.setDefaultButton(true);\n        okButton.setOnAction(evt -> {\n            popup.hide();\n            popup.fireEvent(new RecurrencePopupEvent(RecurrencePopupEvent.OK_PRESSED));\n        });\n\n        Button cancelButton = new Button(Messages.getString(\"RecurrencePopupSkin.CANCEL\"));\n        cancelButton.setCancelButton(true);\n        cancelButton.setOnAction(evt -> {\n            popup.hide();\n            popup.fireEvent(new RecurrencePopupEvent(RecurrencePopupEvent.CANCEL_PRESSED));\n        });\n\n        HBox buttonBox = new HBox();\n        buttonBox.setAlignment(Pos.CENTER);\n        buttonBox.getChildren().addAll(cancelButton, okButton);\n        buttonBox.getStyleClass().add(\"button-pane\");\n\n        BorderPane contentPane = new BorderPane();\n        contentPane.getStyleClass().add(\"content\");\n        contentPane.setCenter(popup.getRecurrenceView());\n        contentPane.setBottom(buttonBox);\n\n        stackPane = popup.getRoot();\n        stackPane.getChildren().add(contentPane);\n    }\n\n    @Override\n    public RecurrencePopup getSkinnable() {\n        return popup;\n    }\n\n    @Override\n    public Node getNode() {\n        return stackPane;\n    }\n\n    @Override\n    public void dispose() {\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/OptionsViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.OptionsView;\nimport com.calendarfx.view.print.ViewType;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.VBox;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class OptionsViewSkin extends SkinBase<OptionsView> {\n\n    private final VBox container;\n\n    private final CheckBox allDayEventsChk;\n    private final CheckBox detailsChk;\n    private final CheckBox timedEventsChk;\n    private final CheckBox miniCalendarChk;\n    private final CheckBox calendarKeysChk;\n    private final CheckBox swimlaneLayoutChk;\n\n    public OptionsViewSkin(OptionsView control) {\n        super(control);\n\n        control.viewTypeProperty().addListener(obs -> layout());\n\n        container = new VBox(5);\n\n        allDayEventsChk = new CheckBox(Messages.getString(\"OptionsViewSkin.ALL_DAY_EVENTS_LABEL\"));\n        allDayEventsChk.selectedProperty().bindBidirectional(control.showAllDayEntriesProperty());\n\n        detailsChk = new CheckBox(Messages.getString(\"OptionsViewSkin.DETAILS_LABEL\"));\n        detailsChk.selectedProperty().bindBidirectional(control.showEntryDetailsProperty());\n\n        timedEventsChk = new CheckBox(Messages.getString(\"OptionsViewSkin.TIMED_EVENTS_LABEL\"));\n        timedEventsChk.selectedProperty().bindBidirectional(control.showTimedEntriesProperty());\n\n        miniCalendarChk = new CheckBox(Messages.getString(\"OptionsViewSkin.MINI_CALENDAR_LABEL\"));\n        miniCalendarChk.selectedProperty().bindBidirectional(control.showMiniCalendarsProperty());\n\n        calendarKeysChk = new CheckBox(Messages.getString(\"OptionsViewSkin.CALENDAR_KEYS_LABEL\"));\n        calendarKeysChk.selectedProperty().bindBidirectional(control.showCalendarKeysProperty());\n\n        swimlaneLayoutChk = new CheckBox(Messages.getString(\"OptionsViewSkin.SWIMLANE_LAYOUT_LABEL\"));\n        swimlaneLayoutChk.selectedProperty().bindBidirectional(control.showSwimlaneLayoutProperty());\n\n        layout();\n        getChildren().add(container);\n    }\n\n    private void layout() {\n        List<Node> children = new ArrayList<>();\n\n        if (getSkinnable().getViewType() == ViewType.DAY_VIEW) {\n            children.add(allDayEventsChk);\n            children.add(detailsChk);\n            children.add(miniCalendarChk);\n            children.add(calendarKeysChk);\n            children.add(swimlaneLayoutChk);\n        } else if (getSkinnable().getViewType() == ViewType.WEEK_VIEW) {\n            children.add(allDayEventsChk);\n            children.add(miniCalendarChk);\n            children.add(calendarKeysChk);\n            children.add(swimlaneLayoutChk);\n        } else if (getSkinnable().getViewType() == ViewType.MONTH_VIEW) {\n            children.add(allDayEventsChk);\n            children.add(timedEventsChk);\n            children.add(miniCalendarChk);\n            children.add(calendarKeysChk);\n        }\n\n        container.getChildren().setAll(children);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/PaperViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.PaperView;\nimport com.calendarfx.view.print.ViewType;\n\nimport impl.com.calendarfx.view.NumericTextField;\nimport javafx.print.Paper;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.util.StringConverter;\nimport javafx.util.converter.NumberStringConverter;\n\npublic class PaperViewSkin extends SkinBase<PaperView> {\n\n    private final GridPane gridPane;\n\n    private GridPane marginsGridPane;\n\n    public PaperViewSkin(PaperView control) {\n        super(control);\n\n        ComboBox<ViewType> viewTypeComboBox = new ComboBox<>();\n        viewTypeComboBox.setMaxWidth(Double.MAX_VALUE);\n        viewTypeComboBox.getItems().setAll(ViewType.values());\n        viewTypeComboBox.valueProperty().bindBidirectional(control.viewTypeProperty());\n        viewTypeComboBox.setConverter(new StringConverter<ViewType>() {\n            @Override\n            public String toString(ViewType object) {\n                return Messages.getString(object.getMessageKey());\n            }\n\n            @Override\n            public ViewType fromString(String string) {\n                if (string != null) {\n                    for (ViewType type : ViewType.values()) {\n                        if (string.equals(Messages.getString(type.getMessageKey()))) {\n                            return type;\n                        }\n                    }\n                }\n                return null;\n            }\n        });\n\n        ComboBox<Paper> paperComboBox = new ComboBox<>();\n        paperComboBox.setMaxWidth(Double.MAX_VALUE);\n        paperComboBox.setItems(control.getAvailablePapers());\n        paperComboBox.valueProperty().bindBidirectional(control.paperProperty());\n        paperComboBox.setConverter(new StringConverter<Paper>() {\n            @Override\n            public String toString(Paper object) {\n                return object.getName();\n            }\n\n            @Override\n            public Paper fromString(String string) {\n                if (string != null) {\n                    for (Paper paper : control.getAvailablePapers()) {\n                        if (string.equals(paper.getName())) {\n                            return paper;\n                        }\n                    }\n                }\n                return null;\n            }\n        });\n\n        ComboBox<PaperView.MarginType> marginTypeComboBox = new ComboBox<>();\n        marginTypeComboBox.setMaxWidth(Double.MAX_VALUE);\n        marginTypeComboBox.getItems().setAll(PaperView.MarginType.values());\n        marginTypeComboBox.valueProperty().bindBidirectional(control.marginTypeProperty());\n        marginTypeComboBox.setConverter(new StringConverter<PaperView.MarginType>() {\n            @Override\n            public String toString(PaperView.MarginType type) {\n                switch (type) {\n                    case CUSTOM:\n                        return Messages.getString(\"Margin.CUSTOM\");\n                    case DEFAULT:\n                        return Messages.getString(\"Margin.DEFAULT\");\n                    case MINIMUM:\n                        return Messages.getString(\"Margin.MINIMUM\");\n                    default:\n                        return \"Unknown margin type\";\n                }\n            }\n\n            @Override\n            public PaperView.MarginType fromString(String string) {\n                /*\n                 * No need to implement this as the user can not \"type\" the value.\n                 */\n                return null;\n            }\n        });\n\n        gridPane = new GridPane();\n        gridPane.getStyleClass().add(\"container\");\n        gridPane.add(new Label(Messages.getString(\"PaperViewSkin.VIEW_TYPE_LABEL\")), 0, 0);\n        gridPane.add(viewTypeComboBox, 1, 0);\n        gridPane.add(new Label(Messages.getString(\"PaperViewSkin.PAPER_LABEL\")), 0, 1);\n        gridPane.add(paperComboBox, 1, 1);\n        \n        if (control.isShowMargin()){\n            gridPane.add(new Label(Messages.getString(\"PaperViewSkin.MARGIN_LABEL\")), 0, 2);\n            gridPane.add(marginTypeComboBox, 1, 2);\n        }\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        ColumnConstraints col2 = new ColumnConstraints();\n        gridPane.getColumnConstraints().addAll(col1, col2);\n\n        GridPane.setFillWidth(paperComboBox, true);\n        GridPane.setFillWidth(viewTypeComboBox, true);\n\n        getChildren().add(gridPane);\n\n        control.marginTypeProperty().addListener(it -> updateVisibility());\n        updateVisibility();\n    }\n\n    private void updateVisibility() {\n        if (getSkinnable().getMarginType().equals(PaperView.MarginType.CUSTOM)) {\n            if (marginsGridPane == null) {\n                /*\n                 * lazy initialization of the margin fields.\n                 */\n                createMarginFields();\n            }\n            gridPane.add(marginsGridPane, 1, 3);\n        } else {\n            if (marginsGridPane != null) {\n                gridPane.getChildren().remove(marginsGridPane);\n            }\n        }\n    }\n\n    private void createMarginFields() {\n        NumericTextField topField = new NumericTextField();\n        NumericTextField rightField = new NumericTextField();\n        NumericTextField bottomField = new NumericTextField();\n        NumericTextField leftField = new NumericTextField();\n\n        StringConverter<Number> converter = new NumberStringConverter();\n\n        leftField.textProperty().bindBidirectional(getSkinnable().leftMarginProperty(), converter);\n        rightField.textProperty().bindBidirectional(getSkinnable().rightMarginProperty(), converter);\n        topField.textProperty().bindBidirectional(getSkinnable().topMarginProperty(), converter);\n        bottomField.textProperty().bindBidirectional(getSkinnable().bottomMarginProperty(), converter);\n\n        marginsGridPane = new GridPane();\n        marginsGridPane.getStyleClass().add(\"custom-fields\");\n        marginsGridPane.add(new Label(Messages.getString(\"MarginSelector.TOP\")), 0, 0);\n        marginsGridPane.add(topField, 1, 0);\n        marginsGridPane.add(new Label(Messages.getString(\"MarginSelector.RIGHT\")), 2, 0);\n        marginsGridPane.add(rightField, 3, 0);\n        marginsGridPane.add(new Label(Messages.getString(\"MarginSelector.BOTTOM\")), 0, 1);\n        marginsGridPane.add(bottomField, 1, 1);\n        marginsGridPane.add(new Label(Messages.getString(\"MarginSelector.LEFT\")), 2, 1);\n        marginsGridPane.add(leftField, 3, 1);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/PreviewPaneSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.PreviewPane;\nimport com.calendarfx.view.print.PrintablePage;\nimport com.calendarfx.view.print.ZoomPane;\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.Slider;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport org.kordamp.ikonli.fontawesome.FontAwesome;\nimport org.kordamp.ikonli.javafx.FontIcon;\n\npublic class PreviewPaneSkin extends SkinBase<PreviewPane> {\n\n    public PreviewPaneSkin(PreviewPane control) {\n        super(control);\n\n        Slider slider = new Slider();\n        slider.setMin(ZoomPane.MIN_ZOOM_VALUE);\n        slider.setMax(ZoomPane.MAX_ZOOM_VALUE);\n        slider.valueProperty().bindBidirectional(control.getZoomPane().zoomProperty());\n\n        BorderPane center = new BorderPane();\n        center.setCenter(control.getZoomPane());\n        center.getStyleClass().add(\"center\");\n\n        PrintablePage page = control.getPrintablePage();\n\n        FontIcon backIcon = new FontIcon(FontAwesome.CHEVRON_LEFT);\n        FontIcon forwardIcon = new FontIcon(FontAwesome.CHEVRON_RIGHT);\n\n        Button backBtn = new Button();\n        backBtn.setGraphic(backIcon);\n        backBtn.setOnAction(evt -> page.back());\n        backBtn.disableProperty().bind(Bindings.equal(1, page.pageNumberProperty()));\n\n        Button nextBtn = new Button();\n        nextBtn.setGraphic(forwardIcon);\n        nextBtn.setOnAction(evt -> page.next());\n        nextBtn.disableProperty().bind(Bindings.equal(page.pageNumberProperty(), page.totalPagesProperty()));\n\n        Label pagesLbl = new Label();\n        pagesLbl.textProperty().bind(Bindings.createStringBinding(() -> page.getPageNumber() + \"/\" + page.getTotalPages(), page.pageNumberProperty(), page.totalPagesProperty()));\n\n        HBox bottom = new HBox(new Label(Messages.getString(\"PreviewPaneSkin.ZOOM_LABEL\")), slider, backBtn, pagesLbl, nextBtn);\n        bottom.setAlignment(Pos.CENTER_RIGHT);\n        bottom.getStyleClass().add(\"footer\");\n\n        BorderPane container = new BorderPane();\n        container.getStyleClass().add(\"container\");\n        container.setCenter(center);\n        container.setBottom(bottom);\n\n        getChildren().add(container);\n    }\n\n}\n\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/PrintViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.PreviewPane;\nimport com.calendarfx.view.print.PrintView;\nimport com.calendarfx.view.print.SettingsView;\nimport javafx.geometry.Orientation;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.RowConstraints;\n\npublic class PrintViewSkin extends SkinBase<PrintView> {\n\n    public PrintViewSkin(PrintView control) {\n        super(control);\n\n        GridPane gridPane = new GridPane();\n        gridPane.getStyleClass().add(\"container\");\n        gridPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n\n        RowConstraints row1 = new RowConstraints();\n        RowConstraints row2 = new RowConstraints();\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        ColumnConstraints col2 = new ColumnConstraints();\n        ColumnConstraints col3 = new ColumnConstraints();\n\n        row1.setVgrow(Priority.ALWAYS);\n        row2.setVgrow(Priority.NEVER);\n\n        col1.setHgrow(Priority.ALWAYS);\n        col2.setHgrow(Priority.NEVER);\n        col3.setHgrow(Priority.NEVER);\n\n        row1.setFillHeight(true);\n        row2.setFillHeight(true);\n\n        col1.setFillWidth(true);\n        col2.setFillWidth(true);\n        col3.setFillWidth(true);\n\n        col1.setMaxWidth(Double.MAX_VALUE);\n        col3.setMaxWidth(Region.USE_PREF_SIZE);\n        col3.setMinWidth(Region.USE_PREF_SIZE);\n\n        row1.setMaxHeight(Double.MAX_VALUE);\n        row2.setMinHeight(Region.USE_PREF_SIZE);\n\n        gridPane.getRowConstraints().setAll(row1, row2);\n        gridPane.getColumnConstraints().setAll(col1, col2, col3);\n\n        // preview pane\n        PreviewPane previewPane = control.getPreviewPane();\n        gridPane.add(previewPane, 0, 0);\n        GridPane.setRowSpan(previewPane, 2);\n\n        // settings\n        SettingsView settingsView = control.getSettingsView();\n        gridPane.add(settingsView, 2, 0);\n\n        // separator\n        Separator separator = new Separator();\n        separator.setOrientation(Orientation.VERTICAL);\n        GridPane.setRowSpan(separator, 2);\n        gridPane.add(separator, 1, 0);\n\n        // button bar\n        Button cancelBtn = new Button(Messages.getString(\"PrintView.CANCEL_BUTTON\"));\n        cancelBtn.onActionProperty().bind(control.onCancelProperty());\n\n        Button continueBtn = new Button(Messages.getString(\"PrintView.CONTINUE_BUTTON\"));\n        continueBtn.onActionProperty().bind(control.onContinueProperty());\n\n        HBox buttonsBar = new HBox();\n        buttonsBar.getStyleClass().add(\"button-bar\");\n        buttonsBar.getChildren().addAll(cancelBtn, continueBtn);\n\n        gridPane.add(buttonsBar, 2, 1);\n\n        getChildren().add(gridPane);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/PrintablePageSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.SourceGridView;\nimport com.calendarfx.view.YearMonthView;\nimport com.calendarfx.view.print.PrintablePage;\nimport com.calendarfx.view.print.ViewType;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyStringProperty;\nimport javafx.beans.property.ReadOnlyStringWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.event.Event;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Region;\nimport javafx.scene.layout.VBox;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class PrintablePageSkin extends SkinBase<PrintablePage> {\n\n    private final YearMonthView calendarOne = new YearMonthView();\n    private final YearMonthView calendarTwo = new YearMonthView();\n\n    public PrintablePageSkin(PrintablePage control) {\n        super(control);\n\n        calendarOne.setShowTodayButton(false);\n        calendarOne.setShowMonthArrows(false);\n        calendarOne.setShowYearArrows(false);\n        calendarOne.setShowWeekNumbers(false);\n        calendarOne.setShowToday(false);\n        calendarOne.weekFieldsProperty().bind(control.weekFieldsProperty());\n        calendarOne.visibleProperty().bind(control.showMiniCalendarsProperty());\n        calendarOne.dateProperty().bind(control.pageStartDateProperty());\n        calendarOne.managedProperty().bind(calendarOne.visibleProperty());\n\n        calendarTwo.setShowTodayButton(false);\n        calendarTwo.setShowMonthArrows(false);\n        calendarTwo.setShowYearArrows(false);\n        calendarTwo.setShowWeekNumbers(false);\n        calendarTwo.setShowToday(false);\n        calendarTwo.weekFieldsProperty().bind(control.weekFieldsProperty());\n        calendarTwo.dateProperty()\n                .bind(Bindings.createObjectBinding(\n                        () -> calendarOne.getDate().plusMonths(1),\n                        calendarOne.dateProperty()));\n        calendarTwo.visibleProperty().bind(control.showMiniCalendarsProperty());\n        calendarTwo.managedProperty().bind(calendarTwo.visibleProperty());\n\n        SourceGridView sourceView = new SourceGridView();\n        sourceView.visibleProperty().bind(control.showCalendarKeysProperty());\n        sourceView.managedProperty().bind(sourceView.visibleProperty());\n        sourceView.bind(control);\n\n        PrintPagePeriodFormatter formatter = new PrintPagePeriodFormatter(\n                control);\n        \n        Label periodLabel = new Label();\n        periodLabel.textProperty().bind(formatter.textProperty());\n        periodLabel.getStyleClass().add(\"period-label\");\n\n        VBox titleSection = new VBox();\n        titleSection.getChildren().addAll(periodLabel, sourceView);\n        titleSection.getStyleClass().add(\"title-section\");\n\n        HBox calendarsBox = new HBox(calendarOne, calendarTwo);\n        calendarsBox.addEventFilter(MouseEvent.MOUSE_CLICKED, Event::consume);\n        calendarsBox.getStyleClass().add(\"mini-calendars\");\n\n        BorderPane header = new BorderPane();\n        header.setCenter(titleSection);\n        header.setRight(calendarsBox);\n        header.getStyleClass().add(\"header\");\n\n        BorderPane container = new BorderPane();\n        container.setTop(header);\n        container.centerProperty().bind(control.viewProperty());\n        container.getStyleClass().add(\"container\");\n        getChildren().add(container);\n\n        InvalidationListener selectedDatesListener = obs -> updateSelectedDates();\n        control.pageStartDateProperty().addListener(selectedDatesListener);\n        control.pageEndDateProperty().addListener(selectedDatesListener);\n        updateSelectedDates();\n\n        Region glassPane = new Region();\n        glassPane.prefWidthProperty().bind(getSkinnable().widthProperty());\n        glassPane.prefHeightProperty().bind(getSkinnable().heightProperty());\n        glassPane.getStyleClass().add(\"glasspane\");\n        glassPane.setMouseTransparent(false);\n        getChildren().add(glassPane);\n    }\n\n    private void updateSelectedDates() {\n        List<LocalDate> dates = new ArrayList<>();\n\n        LocalDate start = getSkinnable().getPageStartDate();\n\n        do {\n            dates.add(start);\n            start = start.plusDays(1);\n        } while (start.isBefore(getSkinnable().getPageEndDate())\n                || start.isEqual(getSkinnable().getPageEndDate()));\n\n        calendarOne.getSelectedDates().clear();\n        calendarOne.getSelectedDates().addAll(dates);\n\n        calendarTwo.getSelectedDates().clear();\n        calendarTwo.getSelectedDates().addAll(dates);\n    }\n\n    private static final class PrintPagePeriodFormatter\n            implements InvalidationListener {\n\n        private final PrintablePage page;\n        private final ObjectProperty<Map<ViewType, DateTimeFormatter>> formatterMapProperty = new SimpleObjectProperty<>(\n                this, \"formatterMapProperty\");\n\n        private PrintPagePeriodFormatter(PrintablePage page) {\n            this.page = page;\n            page.viewTypeProperty().addListener(this);\n            page.pageStartDateProperty().addListener(this);\n            page.pageEndDateProperty().addListener(this);\n            formatterMapProperty.bind(page.formatterMapProperty());\n            format();\n        }\n\n        private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper(\n                this, \"text\");\n\n        public ReadOnlyStringProperty textProperty() {\n            return text.getReadOnlyProperty();\n        }\n\n        private void setText(String text) {\n            this.text.set(text);\n        }\n\n        private void format() {\n            DateTimeFormatter formatter = getFormatterMap()\n                    .get(page.getViewType());\n            if (formatter == null) {\n                formatter = page.getViewType().getDateTimeFormatter();\n            }\n\n            switch (page.getViewType()) {\n            case DAY_VIEW:\n            case MONTH_VIEW:\n                setText(formatter.format(page.getPageStartDate()));\n                break;\n\n            case WEEK_VIEW:\n                String sb = formatter.format(page.getPageStartDate()) +\n                        \" \" +\n                        Messages.getString(\"PrintViewType.TO_LABEL\") +\n                        \" \" +\n                        formatter.format(page.getPageEndDate());\n                setText(sb);\n                break;\n            default:\n                setText(\"\");\n                break;\n            }\n        }\n\n        @Override\n        public void invalidated(Observable observable) {\n            format();\n        }\n\n        public ObjectProperty<Map<ViewType, DateTimeFormatter>> dateTimeFormatterMapProperty() {\n            return formatterMapProperty;\n        }\n\n        private Map<ViewType, DateTimeFormatter> getFormatterMap() {\n            if (dateTimeFormatterMapProperty().get() == null) {\n                formatterMapProperty.set(new HashMap<>());\n            }\n            return dateTimeFormatterMapProperty().get();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/SettingsViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.SettingsView;\n\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\n\npublic class SettingsViewSkin extends SkinBase<SettingsView> {\n\n    public SettingsViewSkin(SettingsView control) {\n        super(control);\n\n        VBox container = new VBox();\n        container.getStyleClass().add(\"container\");\n\n        ScrollPane scrollPane = new ScrollPane(control.getSourceView());\n        scrollPane.setPrefViewportHeight(180);\n\n        container.getChildren()\n                .addAll(new SectionTitle(\n                        Messages.getString(\"PrintViewType.PAPER_TITLE_LABEL\")),\n                        control.getPaperView(),\n                        new SectionTitle(Messages.getString(\"PrintViewType.TIME_RANGE_TITLE_LABEL\")),\n                        control.getTimeRangeView(),\n                        new SectionTitle(Messages.getString(\"PrintViewType.SOURCE_VIEW_TITLE_LABEL\")),\n                        scrollPane,\n                        new SectionTitle(Messages.getString(\"PrintViewType.OPTIONS_TITLE_LABEL\")),\n                        control.getOptionsView());\n\n        container.getChildren().removeIf(x -> {\n            if(!control.getOptionsView().isVisible()){\n                if (x instanceof SectionTitle){\n                    return ((SectionTitle) x).titleLabel.getText().equals(Messages.getString(\"PrintViewType.OPTIONS_TITLE_LABEL\"));\n                }\n            }\n            return false;\n        });\n\n        getChildren().add(container);\n    }\n\n    private static class SectionTitle extends HBox {\n\n        private final Label titleLabel;\n\n        public SectionTitle(String name) {\n            getStyleClass().add(\"section-title\");\n\n            titleLabel = new Label(name);\n            Separator separator = new Separator();\n            separator.setPadding(new Insets(5, 0, 0, 0));\n\n            HBox.setHgrow(separator, Priority.ALWAYS);\n            HBox.setHgrow(titleLabel, Priority.NEVER);\n\n            setSpacing(10);\n            setAlignment(Pos.CENTER);\n\n            getChildren().addAll(titleLabel, separator);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/TimeRangeFieldSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.TimeRangeField;\n\nimport com.calendarfx.view.print.TimeRangeField.TimeRangeFieldValue;\nimport javafx.beans.InvalidationListener;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.util.StringConverter;\n\npublic class TimeRangeFieldSkin extends SkinBase<TimeRangeField> {\n\n    private final DatePicker datePicker;\n    private final Spinner<Integer> weekNumberSpinner;\n    private final Spinner<Integer> monthYearSpinner;\n    private final Spinner<Integer> afterUnitsSpinner;\n    private final Label afterUnitsLabel;\n\n    private final IntegerSpinnerValueFactory weekValueFactory;\n    private final IntegerSpinnerValueFactory monthYearValueFactory;\n    private final IntegerSpinnerValueFactory afterUnitsValueFactory;\n\n    public TimeRangeFieldSkin(TimeRangeField control) {\n        super(control);\n\n        ComboBox<TimeRangeFieldValue> valuesComboBox = new ComboBox<>();\n        valuesComboBox.setConverter(new TimeRangeFieldValueStringConverter());\n        valuesComboBox.setItems(control.getValues());\n        valuesComboBox.valueProperty().bindBidirectional(control.valueProperty());\n        valuesComboBox.setVisibleRowCount(5);\n\n        datePicker = new DatePicker();\n        datePicker.getEditor().setPrefColumnCount(6);\n        datePicker.valueProperty().bindBidirectional(control.onDateProperty());\n        datePicker.managedProperty().bind(datePicker.visibleProperty());\n        datePicker.setEditable(false);\n\n        weekValueFactory = new IntegerSpinnerValueFactory(1, 52);\n        weekValueFactory.valueProperty().addListener(obs -> control.setOnWeekNumber(weekValueFactory.getValue()));\n        control.onWeekNumberProperty().addListener(obs -> {\n            if (control.getOnWeekNumber() != null) {\n                weekValueFactory.setValue(control.getOnWeekNumber());\n            }\n        });\n        weekNumberSpinner = new Spinner<>();\n        weekNumberSpinner.setValueFactory(weekValueFactory);\n        weekNumberSpinner.managedProperty().bind(weekNumberSpinner.visibleProperty());\n        weekNumberSpinner.setPrefWidth(70);\n\n        monthYearValueFactory = new IntegerSpinnerValueFactory(1972, 3000);\n        monthYearValueFactory.valueProperty().addListener(obs -> control.setMonthYear(monthYearValueFactory.getValue()));\n        control.monthYearProperty().addListener(obs -> {\n            if (control.getMonthYear() != null) {\n                monthYearValueFactory.setValue(control.getMonthYear());\n            }\n        });\n\n        monthYearSpinner = new Spinner<>();\n        monthYearSpinner.getEditor().setPrefColumnCount(6);\n        monthYearSpinner.setValueFactory(monthYearValueFactory);\n        monthYearSpinner.managedProperty().bind(monthYearSpinner.visibleProperty());\n\n        afterUnitsLabel = new Label();\n        afterUnitsLabel.managedProperty().bind(afterUnitsLabel.visibleProperty());\n        afterUnitsValueFactory = new IntegerSpinnerValueFactory(1, 500);\n        afterUnitsValueFactory.valueProperty().addListener(obs -> control.setAfterUnits(afterUnitsValueFactory.getValue()));\n        control.afterUnitsProperty().addListener(obs -> {\n            if (control.getAfterUnits() != null) {\n                afterUnitsValueFactory.setValue(control.getAfterUnits());\n                afterUnitsLabel.setText(control.getAfterUnits().equals(1)\n                        ? Messages.getString(getSkinnable().getViewType()\n                                .getSingularChronoMessageKey())\n                        : Messages.getString(getSkinnable().getViewType()\n                                .getPluralChronoMessageKey()));\n            }\n        });\n        afterUnitsSpinner = new Spinner<>();\n        afterUnitsSpinner.getEditor().setPrefColumnCount(4);\n        afterUnitsSpinner.setValueFactory(afterUnitsValueFactory);\n        afterUnitsSpinner.managedProperty().bind(afterUnitsSpinner.visibleProperty());\n\n        InvalidationListener listener = obs -> layout();\n        control.viewTypeProperty().addListener(listener);\n        control.valueProperty().addListener(listener);\n\n        HBox container = new HBox(5, valuesComboBox, datePicker, weekNumberSpinner, monthYearSpinner, afterUnitsSpinner, afterUnitsLabel);\n        container.setAlignment(Pos.CENTER_LEFT);\n        HBox.setHgrow(valuesComboBox, Priority.ALWAYS);\n        HBox.setHgrow(datePicker, Priority.SOMETIMES);\n\n        getChildren().add(container);\n        layout();\n    }\n\n    private void layout() {\n        datePicker.setVisible(false);\n        weekNumberSpinner.setVisible(false);\n        monthYearSpinner.setVisible(false);\n        afterUnitsSpinner.setVisible(false);\n        afterUnitsLabel.setVisible(false);\n\n        if (getSkinnable().getValue() == TimeRangeFieldValue.ON_DATE) {\n            datePicker.setVisible(true);\n        } else if (getSkinnable().getValue() == TimeRangeFieldValue.ON_WEEK_NUMBER) {\n            weekNumberSpinner.setVisible(true);\n            weekValueFactory.setValue(getSkinnable().getOnWeekNumber());\n        } else if (getSkinnable().getValue().isMonthValue()) {\n            monthYearSpinner.setVisible(true);\n            monthYearValueFactory.setValue(getSkinnable().getMonthYear());\n        } else if (getSkinnable().getValue() == TimeRangeFieldValue.AFTER) {\n            afterUnitsLabel.setVisible(true);\n            afterUnitsLabel.setText(Messages.getString(getSkinnable().getViewType().getSingularChronoMessageKey()));\n            afterUnitsSpinner.setVisible(true);\n            afterUnitsValueFactory.setValue(getSkinnable().getAfterUnits());\n        }\n    }\n\n    private static class TimeRangeFieldValueStringConverter extends StringConverter<TimeRangeFieldValue> {\n\n        @Override\n        public String toString(TimeRangeFieldValue value) {\n            if (value != null) {\n                return Messages.getString(value.getMessageKey());\n            }\n            return \"\";\n        }\n\n        @Override\n        public TimeRangeFieldValue fromString(String string) {\n            if (string != null) {\n                for (TimeRangeFieldValue type : TimeRangeFieldValue.values()) {\n                    if (string.equals(Messages.getString(type.getMessageKey()))) {\n                        return type;\n                    }\n                }\n            }\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/TimeRangeViewSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.Messages;\nimport com.calendarfx.view.print.TimeRangeView;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.geometry.HPos;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\n\npublic class TimeRangeViewSkin extends SkinBase<TimeRangeView> {\n\n    public TimeRangeViewSkin(TimeRangeView control) {\n        super(control);\n\n        Label overviewLabel = new Label();\n        overviewLabel.textProperty().bind(Bindings.createStringBinding(() -> {\n            if (control.getUnitsToPrint() == 0) {\n                return \"\";\n            }\n            return Messages.getString(\n                    control.getUnitsToPrint() == 1\n                            ? \"TimeRangeViewSkin.PERIOD_LABEL_SINGULAR\"\n                            : \"TimeRangeViewSkin.PERIOD_LABEL_PLURAL\",\n                    control.getUnitsToPrint(),\n                    control.getUnitsToPrint() == 1\n                            ? Messages.getString(control.getViewType()\n                                    .getSingularChronoMessageKey())\n                            : Messages.getString(control.getViewType()\n                                    .getPluralChronoMessageKey()));\n        }, control.unitsToPrintProperty(), control.viewTypeProperty()));\n\n        GridPane gridPane = new GridPane();\n        gridPane.getStyleClass().add(\"container\");\n        gridPane.add(\n                new Label(Messages.getString(\"TimeRangeViewSkin.START_LABEL\")),\n                0, 0);\n        gridPane.add(control.getStartField(), 1, 0);\n        gridPane.add(\n                new Label(Messages.getString(\"TimeRangeViewSkin.END_LABEL\")), 0,\n                1);\n        gridPane.add(control.getEndField(), 1, 1);\n        gridPane.add(overviewLabel, 1, 2);\n\n        ColumnConstraints col1 = new ColumnConstraints();\n        ColumnConstraints col2 = new ColumnConstraints();\n        gridPane.getColumnConstraints().addAll(col1, col2);\n\n        GridPane.setHalignment(control.getStartField(), HPos.LEFT);\n        GridPane.setHalignment(control.getEndField(), HPos.LEFT);\n\n        GridPane.setFillWidth(control.getStartField(), true);\n        GridPane.setFillWidth(control.getEndField(), true);\n\n        getChildren().add(gridPane);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/print/ZoomPaneSkin.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.print;\n\nimport com.calendarfx.view.print.ZoomPane;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.scene.control.SkinBase;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.Region;\nimport javafx.scene.shape.Rectangle;\n\nimport java.util.Objects;\n\npublic class ZoomPaneSkin extends SkinBase<ZoomPane> {\n\n    private static final double INITIAL_SCALE_FACTOR = 0.98;\n\n    private final DoubleProperty scale = new SimpleDoubleProperty();\n    private final DoubleProperty scaleX = new SimpleDoubleProperty();\n    private final DoubleProperty scaleY = new SimpleDoubleProperty();\n\n    private double mouseX;\n    private double mouseY;\n\n    public ZoomPaneSkin(ZoomPane pane) {\n        super(pane);\n\n        Rectangle clip = new Rectangle();\n        clip.widthProperty().bind(pane.widthProperty());\n        clip.heightProperty().bind(pane.heightProperty());\n\n        pane.setClip(clip);\n        pane.contentProperty().addListener((obs, oldValue, newValue) -> updateView(oldValue, newValue));\n        pane.zoomProperty().addListener(obs -> getSkinnable().requestLayout());\n\n        updateView(null, pane.getContent());\n    }\n\n    private void updateView(Region oldValue, Region newValue) {\n        if (oldValue != null) {\n            oldValue.scaleXProperty().unbind();\n            oldValue.scaleYProperty().unbind();\n            oldValue.setOnMousePressed(null);\n            oldValue.setOnMouseDragged(null);\n        }\n\n        Objects.requireNonNull(newValue);\n        newValue.setManaged(false);\n        newValue.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> {\n            mouseX = evt.getSceneX();\n            mouseY = evt.getSceneY();\n        });\n\n        newValue.addEventFilter(MouseEvent.MOUSE_DRAGGED, evt -> {\n            double deltaX = evt.getSceneX() - mouseX;\n            double deltaY = evt.getSceneY() - mouseY;\n\n            newValue.relocate(newValue.getLayoutX() + deltaX, newValue.getLayoutY() + deltaY);\n\n            mouseX = evt.getSceneX();\n            mouseY = evt.getSceneY();\n        });\n\n        scale.unbind();\n        scale.bind(Bindings.multiply(Bindings.min(scaleX, scaleY), getSkinnable().zoomProperty()));\n\n        scaleX.unbind();\n        scaleX.bind(Bindings.min(1, Bindings.divide(Bindings.multiply(INITIAL_SCALE_FACTOR, getSkinnable().widthProperty()), newValue.widthProperty())));\n\n        scaleY.unbind();\n        scaleY.bind(Bindings.min(1, Bindings.divide(Bindings.multiply(INITIAL_SCALE_FACTOR, getSkinnable().heightProperty()), newValue.heightProperty())));\n\n        newValue.scaleXProperty().bind(scale);\n        newValue.scaleYProperty().bind(scale);\n\n        getChildren().setAll(newValue);\n    }\n\n    @Override\n    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {\n        Region content = Objects.requireNonNull(getSkinnable().getContent());\n\n        double w = content.getPrefWidth();\n        double h = content.getPrefHeight();\n\n        double mx = contentX + (contentWidth / 2);\n        double my = contentY + (contentHeight / 2);\n\n        content.resizeRelocate(mx - w / 2, my - h / 2, w, h);\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/Placement.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.view.EntryViewBase;\n\nimport java.util.Objects;\n\n@SuppressWarnings(\"javadoc\")\npublic final class Placement {\n\n    private final int columnIndex;\n\n    private final int columnCount;\n\n    private final EntryViewBase<?> entryViewBase;\n\n    public Placement(EntryViewBase<?> activity, int columnIndex, int columnCount) {\n        this.entryViewBase = Objects.requireNonNull(activity);\n        this.columnIndex = columnIndex;\n        this.columnCount = columnCount;\n    }\n\n    public EntryViewBase<?> getEntryView() {\n        return entryViewBase;\n    }\n\n    public int getColumnIndex() {\n        return columnIndex;\n    }\n\n    public int getColumnCount() {\n        return columnCount;\n    }\n\n    @Override\n    public String toString() {\n        return \"Placement [columnIndex=\" + columnIndex + \", columnCount=\"\n                + columnCount + \", entry=\" + entryViewBase.getEntry() + \"]\";\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/TimeBoundsCluster.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.EntryViewBase;\n\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class TimeBoundsCluster {\n\n    private List<EntryViewBase<?>> entryViews;\n\n    private ZonedDateTime startTime;\n\n    private ZonedDateTime endTime;\n\n    private List<TimeBoundsColumn> columns;\n\n    public int getColumnCount() {\n        if (columns == null || columns.isEmpty()) {\n            return -1;\n        }\n\n        return columns.size();\n    }\n\n    public void add(EntryViewBase<?> view) {\n        if (entryViews == null) {\n            entryViews = new ArrayList<>();\n        }\n\n        entryViews.add(view);\n\n        Entry<?> entry = view.getEntry();\n\n        ZonedDateTime entryStartTime = entry.getStartAsZonedDateTime();\n        ZonedDateTime entryEndTime = entry.getEndAsZonedDateTime();\n\n        if (entry.isFullDay()) {\n            entryStartTime = entryStartTime.with(LocalTime.MIN);\n            entryEndTime = entryEndTime.with(LocalTime.MAX);\n        }\n\n        if (startTime == null || entryStartTime.isBefore(startTime)) {\n            startTime = entryStartTime;\n        }\n\n        if (endTime == null || entryEndTime.isAfter(endTime)) {\n            endTime = entryEndTime;\n        }\n    }\n\n    public boolean intersects(EntryViewBase<?> view) {\n        if (startTime == null) {\n            /*\n             * The first added activity initializes the cluster.\n             */\n            return true;\n        }\n\n        Entry<?> entry = view.getEntry();\n\n        ZonedDateTime entryStartTime = entry.getStartAsZonedDateTime();\n        ZonedDateTime entryEndTime = entry.getEndAsZonedDateTime();\n\n        if (entry.isFullDay()) {\n            entryStartTime = entryStartTime.with(LocalTime.MIN);\n            entryEndTime = entryEndTime.with(LocalTime.MAX);\n        }\n\n        return entryStartTime.isBefore(endTime) && entryEndTime.isAfter(startTime);\n    }\n\n    public List<Placement> resolve() {\n        if (entryViews == null || entryViews.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        columns = new ArrayList<>();\n        columns.add(new TimeBoundsColumn());\n\n        for (EntryViewBase<?> view : entryViews) {\n\n            boolean added = false;\n\n            // Try to add the activity to an existing column.\n            for (TimeBoundsColumn column : columns) {\n                if (column.hasRoomFor(view)) {\n                    column.add(view);\n                    added = true;\n                    break;\n                }\n            }\n\n            // No column found, create a new column.\n            if (!added) {\n                TimeBoundsColumn column = new TimeBoundsColumn();\n                columns.add(column);\n                column.add(view);\n            }\n        }\n\n        final List<Placement> placements = new ArrayList<>();\n        final int colCount = columns.size();\n\n        for (int col = 0; col < columns.size(); col++) {\n            TimeBoundsColumn column = columns.get(col);\n            for (EntryViewBase<?> view : column.getEntryViews()) {\n                placements.add(new Placement(view, col, colCount));\n            }\n        }\n\n        return placements;\n    }\n\n    public List<TimeBoundsColumn> getColumns() {\n        return columns;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/TimeBoundsColumn.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DraggedEntry;\nimport com.calendarfx.view.EntryViewBase;\n\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class TimeBoundsColumn {\n\n    private List<EntryViewBase<?>> entryViewBases;\n\n    public void add(EntryViewBase<?> view) {\n        if (entryViewBases == null) {\n            entryViewBases = new ArrayList<>();\n        }\n\n        entryViewBases.add(view);\n    }\n\n    public boolean hasRoomFor(EntryViewBase<?> view) {\n        if (entryViewBases == null) {\n            return true;\n        }\n\n        Entry<?> entry = view.getEntry();\n        ZonedDateTime entryStartTime = entry.getStartAsZonedDateTime();\n        ZonedDateTime entryEndTime = entry.getEndAsZonedDateTime();\n\n        if (entry.isFullDay()) {\n            entryStartTime = entryStartTime.with(LocalTime.MIN);\n            entryEndTime = entryEndTime.with(LocalTime.MAX);\n        }\n\n        for (EntryViewBase<?> otherView : entryViewBases) {\n\n            if (isSameEntry(view, otherView)) {\n                continue;\n            }\n\n            Entry<?> otherEntry = otherView.getEntry();\n            ZonedDateTime otherEntryStartTime = otherEntry.getStartAsZonedDateTime();\n            ZonedDateTime otherEntryEndTime = otherEntry.getEndAsZonedDateTime();\n\n            if (entry.isFullDay()) {\n                otherEntryStartTime = otherEntryStartTime.with(LocalTime.MIN);\n                otherEntryEndTime = otherEntryEndTime.with(LocalTime.MAX);\n            }\n\n            if (Util.intersect(entryStartTime, entryEndTime,\n                    otherEntryStartTime, otherEntryEndTime)) {\n\n                /*\n                 * The two activities intersect, so we can not use this column\n                 * for the passed activity.\n                 */\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private boolean isSameEntry(EntryViewBase<?> viewA, EntryViewBase<?> viewB) {\n        Entry<?> entryA = viewA.getEntry();\n        Entry<?> entryB = viewB.getEntry();\n\n        if (entryA instanceof DraggedEntry) {\n            return isSameEntry((DraggedEntry) entryA, entryB);\n        }\n\n        if (entryB instanceof DraggedEntry) {\n            return isSameEntry((DraggedEntry) entryB, entryA);\n        }\n\n        return false;\n    }\n\n    private boolean isSameEntry(DraggedEntry draggedEntry, Entry<?> entry) {\n        if (entry.isRecurrence()) {\n            return draggedEntry.getOriginalEntry().getRecurrenceSourceEntry() == entry.getRecurrenceSourceEntry();\n        }\n\n        return draggedEntry.getOriginalEntry() == entry;\n    }\n\n    public List<EntryViewBase<?>> getEntryViews() {\n        return entryViewBases;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/TimeBoundsResolver.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.view.EntryViewBase;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class TimeBoundsResolver {\n\n    public TimeBoundsResolver() {\n    }\n\n    private static Comparator<EntryViewBase<?>> additionalComparator;\n\n    /**\n     * The resolver always sorts entries based on their time bounds but when\n     * entries have the same bounds the application might want to sort them based on\n     * additional criteria. This can be implemented this way.\n     *\n     * @return\n     */\n    public static Comparator<EntryViewBase<?>> getAdditionalComparator() {\n        return additionalComparator;\n    }\n\n    public static void setAdditionalComparator(Comparator<EntryViewBase<?>> additionalComparator) {\n        TimeBoundsResolver.additionalComparator = additionalComparator;\n    }\n\n    public static <T extends EntryViewBase> List<Placement> resolve(List<T> entryViews) {\n\n        final Comparator<T> comparator = (o1, o2) -> {\n\n            int result = o1.compareTo(o2);\n\n            if (result == 0 && additionalComparator != null) {\n                return additionalComparator.compare(o1, o2);\n            }\n\n            return 0;\n        };\n\n        entryViews.sort(comparator);\n\n        List<Placement> placements = new ArrayList<>();\n        List<TimeBoundsCluster> clusters = new ArrayList<>();\n\n        TimeBoundsCluster cluster = null;\n\n        for (T view : entryViews) {\n            if (view.isVisible()) {\n                if (cluster == null || !cluster.intersects(view)) {\n                    cluster = new TimeBoundsCluster();\n                    clusters.add(cluster);\n                }\n\n                cluster.add(view);\n            }\n        }\n\n        for (TimeBoundsCluster c : new ArrayList<>(clusters)) {\n            placements.addAll(c.resolve());\n        }\n\n        return placements;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/Util.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.view.DateControl;\nimport com.calendarfx.view.Messages;\nimport javafx.application.Platform;\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.Observable;\nimport javafx.beans.WeakListener;\nimport javafx.beans.property.Property;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Orientation;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.control.MultipleSelectionModel;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.layout.Pane;\nimport net.fortuna.ical4j.model.Recur;\nimport net.fortuna.ical4j.model.WeekDay;\nimport net.fortuna.ical4j.transform.recurrence.Frequency;\n\nimport java.lang.ref.WeakReference;\nimport java.text.MessageFormat;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\nimport java.time.format.FormatStyle;\nimport java.time.temporal.ChronoField;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport static java.time.temporal.ChronoField.DAY_OF_WEEK;\nimport static java.time.temporal.ChronoField.DAY_OF_YEAR;\nimport static java.time.temporal.ChronoField.HOUR_OF_DAY;\nimport static java.time.temporal.ChronoField.MICRO_OF_SECOND;\nimport static java.time.temporal.ChronoField.MILLI_OF_SECOND;\nimport static java.time.temporal.ChronoField.MINUTE_OF_HOUR;\nimport static java.time.temporal.ChronoField.MONTH_OF_YEAR;\nimport static java.time.temporal.ChronoField.NANO_OF_SECOND;\nimport static java.time.temporal.ChronoField.SECOND_OF_MINUTE;\nimport static java.time.temporal.ChronoUnit.DAYS;\n\n/**\n * A collection of useful static methods for easy re-use in various\n * places of the framework.\n */\npublic final class Util {\n\n    public static boolean removeChildren(Pane parent, Predicate<Node> predicate) {\n        List<Node> list = new ArrayList<>(parent.getChildrenUnmodifiable().stream().filter(predicate.negate()).collect(Collectors.toList()));\n        boolean childrenWereRemoved = list.removeIf(predicate);\n        if (list.isEmpty()) {\n            parent.getChildren().clear();\n        } else {\n            parent.getChildren().setAll(list);\n        }\n        return childrenWereRemoved;\n    }\n\n    public static boolean removeChildren(Group group, Predicate<Node> predicate) {\n        List<Node> list = new ArrayList<>(group.getChildren());\n        boolean childrenWereRemoved = list.removeIf(predicate);\n        if (list.isEmpty()) {\n            if (!group.getChildren().isEmpty()) {\n                group.getChildren().clear();\n            }\n        } else {\n            group.getChildren().setAll(list);\n        }\n        return childrenWereRemoved;\n    }\n\n    public static boolean intersect(LocalDate aStart, LocalDate aEnd, LocalDate bStart, LocalDate bEnd) {\n        // Same start time or same end time?\n        if (aStart.equals(bStart) || aEnd.equals(bEnd)) {\n            return true;\n        }\n\n        return aStart.isBefore(bEnd) && aEnd.isAfter(bStart);\n    }\n\n\n    public static boolean intersect(LocalTime aStart, LocalTime aEnd, LocalTime bStart, LocalTime bEnd) {\n        // Same start time or same end time?\n        if (aStart.equals(bStart) || aEnd.equals(bEnd)) {\n            return true;\n        }\n\n        return aStart.isBefore(bEnd) && aEnd.isAfter(bStart);\n    }\n\n    public static boolean intersect(ZonedDateTime aStart, ZonedDateTime aEnd, ZonedDateTime bStart, ZonedDateTime bEnd) {\n        // Same start time or same end time?\n        if (aStart.equals(bStart) || aEnd.equals(bEnd)) {\n            return true;\n        }\n\n        return aStart.isBefore(bEnd) && aEnd.isAfter(bStart);\n\n    }\n\n    public static LocalDateTime truncate(LocalDateTime time, ChronoUnit unit, int stepRate, DayOfWeek firstDayOfWeek) {\n        switch (unit) {\n            case DAYS:\n                return adjustField(time, DAY_OF_YEAR, stepRate).truncatedTo(unit);\n            case HALF_DAYS:\n                return time.truncatedTo(unit);\n            case HOURS:\n                return adjustField(time, HOUR_OF_DAY, stepRate).truncatedTo(unit);\n            case MINUTES:\n                return adjustField(time, MINUTE_OF_HOUR, stepRate).truncatedTo(unit);\n            case SECONDS:\n                return adjustField(time, SECOND_OF_MINUTE, stepRate).truncatedTo(unit);\n            case MILLIS:\n                return adjustField(time, MILLI_OF_SECOND, stepRate).truncatedTo(unit);\n            case MICROS:\n                return adjustField(time, MICRO_OF_SECOND, stepRate).truncatedTo(unit);\n            case NANOS:\n                return adjustField(time, NANO_OF_SECOND, stepRate).truncatedTo(unit);\n            case MONTHS:\n                return time.with(MONTH_OF_YEAR, Math.max(1, time.get(MONTH_OF_YEAR) - time.get(MONTH_OF_YEAR) % stepRate)).withDayOfMonth(1).truncatedTo(DAYS);\n            case YEARS:\n                return adjustField(time, ChronoField.YEAR, stepRate).withDayOfYear(1).truncatedTo(DAYS);\n            case WEEKS:\n                return time.with(DAY_OF_WEEK, firstDayOfWeek.getValue()).truncatedTo(DAYS);\n            case DECADES:\n                int decade = time.getYear() / 10 * 10;\n                return time.with(ChronoField.YEAR, decade).withDayOfYear(1).truncatedTo(DAYS);\n            case CENTURIES:\n                int century = time.getYear() / 100 * 100;\n                return time.with(ChronoField.YEAR, century).withDayOfYear(1).truncatedTo(DAYS);\n            case MILLENNIA:\n                int millenium = time.getYear() / 1000 * 1000;\n                return time.with(ChronoField.YEAR, millenium).withDayOfYear(1).truncatedTo(DAYS);\n            default:\n        }\n\n        return time;\n    }\n\n    public static ZonedDateTime truncate(ZonedDateTime time, ChronoUnit unit, int stepRate, DayOfWeek firstDayOfWeek) {\n        switch (unit) {\n            case DAYS:\n                return adjustField(time, DAY_OF_YEAR, stepRate).truncatedTo(unit);\n            case HALF_DAYS:\n                return time.truncatedTo(unit);\n            case HOURS:\n                return adjustField(time, HOUR_OF_DAY, stepRate).truncatedTo(unit);\n            case MINUTES:\n                return adjustField(time, MINUTE_OF_HOUR, stepRate).truncatedTo(unit);\n            case SECONDS:\n                return adjustField(time, SECOND_OF_MINUTE, stepRate).truncatedTo(unit);\n            case MILLIS:\n                return adjustField(time, MILLI_OF_SECOND, stepRate).truncatedTo(unit);\n            case MICROS:\n                return adjustField(time, MICRO_OF_SECOND, stepRate).truncatedTo(unit);\n            case NANOS:\n                return adjustField(time, NANO_OF_SECOND, stepRate).truncatedTo(unit);\n            case MONTHS:\n                return time.with(MONTH_OF_YEAR, Math.max(1, time.get(MONTH_OF_YEAR) - time.get(MONTH_OF_YEAR) % stepRate)).withDayOfMonth(1).truncatedTo(DAYS);\n            case YEARS:\n                return adjustField(time, ChronoField.YEAR, stepRate).withDayOfYear(1).truncatedTo(DAYS);\n            case WEEKS:\n                return time.with(DAY_OF_WEEK, firstDayOfWeek.getValue()).truncatedTo(DAYS);\n            case DECADES:\n                int decade = time.getYear() / 10 * 10;\n                return time.with(ChronoField.YEAR, decade).withDayOfYear(1).truncatedTo(DAYS);\n            case CENTURIES:\n                int century = time.getYear() / 100 * 100;\n                return time.with(ChronoField.YEAR, century).withDayOfYear(1).truncatedTo(DAYS);\n            case MILLENNIA:\n                int millenium = time.getYear() / 1000 * 1000;\n                return time.with(ChronoField.YEAR, millenium).withDayOfYear(1).truncatedTo(DAYS);\n            default:\n        }\n\n        return time;\n    }\n\n    public static LocalTime truncate(LocalTime time, ChronoUnit unit, int stepRate) {\n        switch (unit) {\n            case HOURS:\n                return adjustField(time, HOUR_OF_DAY, stepRate).truncatedTo(unit);\n            case MINUTES:\n                return adjustField(time, MINUTE_OF_HOUR, stepRate).truncatedTo(unit);\n            case SECONDS:\n                return adjustField(time, SECOND_OF_MINUTE, stepRate).truncatedTo(unit);\n            case MILLIS:\n                return adjustField(time, MILLI_OF_SECOND, stepRate).truncatedTo(unit);\n            case MICROS:\n                return adjustField(time, MICRO_OF_SECOND, stepRate).truncatedTo(unit);\n            case NANOS:\n                return adjustField(time, NANO_OF_SECOND, stepRate).truncatedTo(unit);\n            default:\n        }\n\n        return time;\n    }\n\n    public static void runInFXThread(Runnable runnable) {\n        if (Platform.isFxApplicationThread()) {\n            runnable.run();\n        } else {\n            Platform.runLater(runnable);\n        }\n    }\n\n    private static ZonedDateTime adjustField(ZonedDateTime time, ChronoField field, int stepRate) {\n        return time.with(field, time.get(field) - time.get(field) % stepRate);\n    }\n\n    private static LocalDateTime adjustField(LocalDateTime time, ChronoField field, int stepRate) {\n        return time.with(field, time.get(field) - time.get(field) % stepRate);\n    }\n\n    private static LocalTime adjustField(LocalTime time, ChronoField field, int stepRate) {\n        return time.with(field, time.get(field) - time.get(field) % stepRate);\n    }\n\n    public static <T> MultipleSelectionModel<T> createEmptySelectionModel() {\n        return new EmptySelectionModel<>();\n    }\n\n    private static class EmptySelectionModel<T> extends MultipleSelectionModel<T> {\n        @Override\n        public void selectPrevious() {\n        }\n\n        @Override\n        public void selectNext() {\n        }\n\n        @Override\n        public void select(int index) {\n        }\n\n        @Override\n        public void select(T obj) {\n        }\n\n        @Override\n        public boolean isSelected(int index) {\n            return false;\n        }\n\n        @Override\n        public boolean isEmpty() {\n            return true;\n        }\n\n        @Override\n        public void clearSelection(int index) {\n        }\n\n        @Override\n        public void clearSelection() {\n        }\n\n        @Override\n        public void clearAndSelect(int index) {\n        }\n\n        @Override\n        public void selectLast() {\n        }\n\n        @Override\n        public void selectIndices(int index, int... indices) {\n        }\n\n        @Override\n        public void selectFirst() {\n        }\n\n        @Override\n        public void selectAll() {\n        }\n\n        private final ObservableList<T> selectedItems = FXCollections.observableArrayList();\n\n        @Override\n        public ObservableList<T> getSelectedItems() {\n            return selectedItems;\n        }\n\n        private final ObservableList<Integer> selectedIndices = FXCollections.observableArrayList();\n\n        @Override\n        public ObservableList<Integer> getSelectedIndices() {\n            return selectedIndices;\n        }\n    }\n\n    /**\n     * An interface used for converting an object of one type to an object\n     * of another type.\n     *\n     * @param <L> the first (left) type\n     * @param <R> the second (right) type\n     */\n    public interface Converter<L, R> {\n\n        L toLeft(R right);\n\n        R toRight(L left);\n    }\n\n    /**\n     * Converts the given recurrence rule (according to RFC 2445) into a human-readable text,\n     * e.g. \"RRULE:FREQ=DAILY;\" becomes \"Every day\".\n     *\n     * @param rrule     the rule\n     * @param startDate the start date for the rule\n     * @return a nice text describing the rule\n     */\n    public static String convertRFC2445ToText(String rrule, LocalDate startDate) {\n        try {\n            Recur<LocalDate> rule = new Recur<>(rrule.replaceFirst(\"^RRULE:\", \"\"));\n            StringBuilder sb = new StringBuilder();\n\n            String granularity;\n            String granularities;\n\n            switch (rule.getFrequency()) {\n                case DAILY:\n                    granularity = Messages.getString(\"Util.DAY\");\n                    granularities = Messages.getString(\"Util.DAYS\");\n                    break;\n                case MONTHLY:\n                    granularity = Messages.getString(\"Util.MONTH\");\n                    granularities = Messages.getString(\"Util.MONTHS\");\n                    break;\n                case WEEKLY:\n                    granularity = Messages.getString(\"Util.WEEK\");\n                    granularities = Messages.getString(\"Util.WEEKS\");\n                    break;\n                case YEARLY:\n                    granularity = Messages.getString(\"Util.YEAR\");\n                    granularities = Messages.getString(\"Util.YEARS\");\n                    break;\n                case HOURLY:\n                    granularity = Messages.getString(\"Util.HOUR\");\n                    granularities = Messages.getString(\"Util.HOURS\");\n                    break;\n                case MINUTELY:\n                    granularity = Messages.getString(\"Util.MINUTE\");\n                    granularities = Messages.getString(\"Util.MINUTES\");\n                    break;\n                case SECONDLY:\n                    granularity = Messages.getString(\"Util.SECOND\");\n                    granularities = Messages.getString(\"Util.SECONDS\");\n                    break;\n                default:\n                    granularity = \"\";\n                    granularities = \"\";\n            }\n\n            int interval = rule.getInterval();\n            if (interval > 1) {\n                sb.append(MessageFormat.format(Messages.getString(\"Util.EVERY_PLURAL\"), rule.getInterval(), granularities));\n            } else {\n                sb.append(MessageFormat.format(Messages.getString(\"Util.EVERY_SINGULAR\"), granularity));\n            }\n\n            /*\n             * Weekdays\n             */\n\n            if (rule.getFrequency().equals(Frequency.WEEKLY)) {\n                List<WeekDay> byDay = rule.getDayList();\n                if (!byDay.isEmpty()) {\n                    sb.append(Messages.getString(\"Util.ON_WEEKDAY\"));\n                    for (int i = 0; i < byDay.size(); i++) {\n                        WeekDay num = byDay.get(i);\n                        sb.append(makeHuman(num.getDay()));\n                        if (i < byDay.size() - 1) {\n                            sb.append(\", \");\n                        }\n                    }\n                }\n            }\n\n            if (rule.getFrequency().equals(Frequency.MONTHLY)) {\n\n                if (!rule.getMonthDayList().isEmpty()) {\n\n                    int day = rule.getMonthDayList().get(0);\n                    sb.append(Messages.getString(\"Util.ON_MONTH_DAY\"));\n                    sb.append(day);\n\n                } else if (!rule.getDayList().isEmpty()) {\n\n                    /*\n                     * We only support one day.\n                     */\n                    WeekDay num = rule.getDayList().get(0);\n                    sb.append(MessageFormat.format(Messages.getString(\"Util.ON_MONTH_WEEKDAY\"), makeHuman(num.getOffset()), makeHuman(num.getDay())));\n                }\n            }\n\n            if (rule.getFrequency().equals(Frequency.YEARLY)) {\n                sb.append(MessageFormat.format(Messages.getString(\"Util.ON_DATE\"), DateTimeFormatter.ofPattern(Messages.getString(\"Util.MONTH_AND_DAY_FORMAT\")).format(startDate)));\n            }\n\n            int count = rule.getCount();\n            if (count > 0) {\n                if (count == 1) {\n                    return Messages.getString(\"Util.ONCE\");\n                } else {\n                    sb.append(MessageFormat.format(Messages.getString(\"Util.TIMES\"), count));\n                }\n            } else {\n                LocalDate until = rule.getUntil();\n                if (until != null) {\n                    sb.append(MessageFormat.format(Messages.getString(\"Util.UNTIL_DATE\"), DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(until)));\n                }\n            }\n\n            return sb.toString();\n        } catch (IllegalArgumentException | DateTimeParseException e) {\n            e.printStackTrace();\n            return Messages.getString(\"Util.INVALID_RULE\");\n        }\n    }\n\n    private static String makeHuman(WeekDay.Day wday) {\n        switch (wday) {\n            case FR:\n                return Messages.getString(\"Util.FRIDAY\");\n            case MO:\n                return Messages.getString(\"Util.MONDAY\");\n            case SA:\n                return Messages.getString(\"Util.SATURDAY\");\n            case SU:\n                return Messages.getString(\"Util.SUNDAY\");\n            case TH:\n                return Messages.getString(\"Util.THURSDAY\");\n            case TU:\n                return Messages.getString(\"Util.TUESDAY\");\n            case WE:\n                return Messages.getString(\"Util.WEDNESDAY\");\n            default:\n                throw new IllegalArgumentException(\"unknown weekday: \" + wday);\n        }\n    }\n\n    private static String makeHuman(int num) {\n        switch (num) {\n            case 1:\n                return Messages.getString(\"Util.FIRST\");\n            case 2:\n                return Messages.getString(\"Util.SECOND\");\n            case 3:\n                return Messages.getString(\"Util.THIRD\");\n            case 4:\n                return Messages.getString(\"Util.FOURTH\");\n            case 5:\n                return Messages.getString(\"Util.FIFTH\");\n            default:\n                return Integer.toString(num);\n        }\n    }\n\n    /**\n     * Searches for a {@link ScrollBar} of the given orientation (vertical, horizontal)\n     * somewhere in the containment hierarchy of the given parent node.\n     *\n     * @param parent      the parent node\n     * @param orientation the orientation (horizontal, vertical)\n     * @return a scrollbar or null if none can be found\n     */\n    public static ScrollBar findScrollBar(Parent parent, Orientation orientation) {\n        for (Node node : parent.getChildrenUnmodifiable()) {\n            if (node instanceof ScrollBar) {\n                ScrollBar b = (ScrollBar) node;\n                if (b.getOrientation().equals(orientation)) {\n                    return b;\n                }\n            }\n\n            if (node instanceof Parent) {\n                ScrollBar b = findScrollBar((Parent) node, orientation);\n                if (b != null) {\n                    return b;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Adjusts the given date to a new date that marks the beginning of the week where the\n     * given date is located. If \"Monday\" is the first day of the week and the given date\n     * is a \"Wednesday\" then this method will return a date that is two days earlier than the\n     * given date.\n     *\n     * @param date           the date to adjust\n     * @param firstDayOfWeek the day of week that is considered the start of the week (\"Monday\" in Germany, \"Sunday\" in the US)\n     * @return the date of the first day of the week\n     * @see #adjustToLastDayOfWeek(LocalDate, DayOfWeek)\n     * @see DateControl#getFirstDayOfWeek()\n     */\n    public static LocalDate adjustToFirstDayOfWeek(LocalDate date, DayOfWeek firstDayOfWeek) {\n        LocalDate newDate = date.with(DAY_OF_WEEK, firstDayOfWeek.getValue());\n        if (newDate.isAfter(date)) {\n            newDate = newDate.minusWeeks(1);\n        }\n\n        return newDate;\n    }\n\n    /**\n     * Adjusts the given date to a new date that marks the end of the week where the\n     * given date is located. If \"Monday\" is the first day of the week and the given date\n     * is a \"Wednesday\" then this method will return a date that is four days later than the\n     * given date. This method calculates the first day of the week and then adds six days\n     * to it.\n     *\n     * @param date           the date to adjust\n     * @param firstDayOfWeek the day of week that is considered the start of the week (\"Monday\" in Germany, \"Sunday\" in the US)\n     * @return the date of the first day of the week\n     * @see #adjustToFirstDayOfWeek(LocalDate, DayOfWeek)\n     * @see DateControl#getFirstDayOfWeek()\n     */\n    public static LocalDate adjustToLastDayOfWeek(LocalDate date, DayOfWeek firstDayOfWeek) {\n        LocalDate startOfWeek = adjustToFirstDayOfWeek(date, firstDayOfWeek);\n        return startOfWeek.plusDays(6);\n    }\n\n    /**\n     * Creates a bidirectional binding between the two given properties of different types via the\n     * help of a {@link Converter}.\n     *\n     * @param leftProperty  the left property\n     * @param rightProperty the right property\n     * @param converter     the converter\n     * @param <L>           the type of the left property\n     * @param <R>           the type of the right property\n     */\n    public static <L, R> void bindBidirectional(Property<L> leftProperty, Property<R> rightProperty, Converter<L, R> converter) {\n        BidirectionalConversionBinding<L, R> binding = new BidirectionalConversionBinding<>(leftProperty, rightProperty, converter);\n        leftProperty.addListener(binding);\n        rightProperty.addListener(binding);\n        leftProperty.setValue(converter.toLeft(rightProperty.getValue()));\n    }\n\n    private static class BidirectionalConversionBinding<L, R> implements InvalidationListener, WeakListener {\n\n        private final WeakReference<Property<L>> leftReference;\n        private final WeakReference<Property<R>> rightReference;\n        private final Converter<L, R> converter;\n        private boolean updating;\n\n        private BidirectionalConversionBinding(Property<L> leftProperty, Property<R> rightProperty, Converter<L, R> converter) {\n            this.leftReference = new WeakReference<>(Objects.requireNonNull(leftProperty));\n            this.rightReference = new WeakReference<>(Objects.requireNonNull(rightProperty));\n            this.converter = Objects.requireNonNull(converter);\n        }\n\n        public Property<L> getLeftProperty() {\n            return leftReference.get();\n        }\n\n        public Property<R> getRightProperty() {\n            return rightReference.get();\n        }\n\n        @Override\n        public boolean wasGarbageCollected() {\n            return getLeftProperty() == null || getRightProperty() == null;\n        }\n\n        @Override\n        public void invalidated(Observable observable) {\n            if (updating) {\n                return;\n            }\n\n            final Property<L> leftProperty = getLeftProperty();\n            final Property<R> rightProperty = getRightProperty();\n\n            if (wasGarbageCollected()) {\n                if (leftProperty != null) {\n                    leftProperty.removeListener(this);\n                }\n                if (rightProperty != null) {\n                    rightProperty.removeListener(this);\n                }\n            } else {\n                try {\n                    updating = true;\n\n                    if (observable == leftProperty) {\n                        rightProperty.setValue(converter.toRight(leftProperty.getValue()));\n                    } else {\n                        leftProperty.setValue(converter.toLeft(rightProperty.getValue()));\n                    }\n                } finally {\n                    updating = false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/VisualBoundsCluster.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.EntryViewBase;\nimport impl.com.calendarfx.view.util.VisualBoundsResolver.Range;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class VisualBoundsCluster {\n\n    private List<EntryViewBase<?>> entryViews;\n\n    private Range clusterRange;\n\n    private List<VisualBoundsColumn> columns;\n\n    public int getColumnCount() {\n        if (columns == null || columns.isEmpty()) {\n            return -1;\n        }\n\n        return columns.size();\n    }\n\n    public void add(EntryViewBase<?> entryView, DayView dayView, double contentWidth) {\n        if (entryViews == null) {\n            entryViews = new ArrayList<>();\n        }\n\n        entryViews.add(entryView);\n\n        Entry<?> entry = entryView.getEntry();\n\n        Range entryRange = VisualBoundsResolver.getRange(entryView, dayView, contentWidth);\n\n        if (entry.isFullDay()) {\n            entryRange.y1 = 0;\n            entryRange.y2 = dayView.getHeight();\n        }\n\n        if (clusterRange == null) {\n            clusterRange = new Range();\n            clusterRange.title = \"Cluster Range\";\n            clusterRange.y1 = entryRange.y1;\n            clusterRange.y2 = entryRange.y2;\n        } else {\n            clusterRange.y1 = Math.min(clusterRange.y1, entryRange.y1);\n            clusterRange.y2 = Math.max(clusterRange.y2, entryRange.y2);\n        }\n    }\n\n    public boolean intersects(EntryViewBase<?> entryView, DayView dayView, double contentWidth) {\n        if (clusterRange == null) {\n            /*\n             * The first added activity initializes the cluster.\n             */\n            return true;\n        }\n\n        Entry<?> entry = entryView.getEntry();\n\n        Range entryRange = VisualBoundsResolver.getRange(entryView, dayView, contentWidth);\n\n        if (entry.isFullDay()) {\n            entryRange.y1 = 0;\n            entryRange.y2 = dayView.getHeight();\n        }\n\n        return entryRange.y1 < clusterRange.y2 && entryRange.y2 > clusterRange.y1;\n    }\n\n    public List<Placement> resolve(DayView dayView, double contentWidth) {\n        if (entryViews == null || entryViews.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        columns = new ArrayList<>();\n        columns.add(new VisualBoundsColumn());\n\n        for (EntryViewBase<?> entryView : entryViews) {\n\n            boolean added = false;\n\n            // Try to add the activity to an existing column.\n            for (VisualBoundsColumn column : columns) {\n                if (column.hasRoomFor(entryView, dayView, contentWidth)) {\n                    column.add(entryView);\n                    added = true;\n                    break;\n                }\n            }\n\n            // No column found, create a new column.\n            if (!added) {\n                VisualBoundsColumn column = new VisualBoundsColumn();\n                columns.add(column);\n                column.add(entryView);\n            }\n        }\n\n        final List<Placement> placements = new ArrayList<>();\n        final int colCount = columns.size();\n\n        for (int col = 0; col < columns.size(); col++) {\n            VisualBoundsColumn column = columns.get(col);\n            for (EntryViewBase<?> view : column.getEntryViews()) {\n                placements.add(new Placement(view, col, colCount));\n            }\n        }\n\n        return placements;\n    }\n\n    public List<VisualBoundsColumn> getColumns() {\n        return columns;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/VisualBoundsColumn.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.DraggedEntry;\nimport com.calendarfx.view.EntryViewBase;\nimport impl.com.calendarfx.view.util.VisualBoundsResolver.Range;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class VisualBoundsColumn {\n\n    private List<EntryViewBase<?>> entryViewBases;\n\n    public void add(EntryViewBase<?> view) {\n        if (entryViewBases == null) {\n            entryViewBases = new ArrayList<>();\n        }\n\n        entryViewBases.add(view);\n    }\n\n    public boolean hasRoomFor(EntryViewBase<?> entryView, DayView dayView, double contentWidth) {\n        if (entryViewBases == null) {\n            return true;\n        }\n\n        Entry<?> entry = entryView.getEntry();\n\n        Range entryRange = VisualBoundsResolver.getRange(entryView, dayView, contentWidth);\n\n        if (entry.isFullDay()) {\n            entryRange.y1 = 0;\n            entryRange.y2 = dayView.getHeight();\n        }\n\n        for (EntryViewBase<?> otherView : entryViewBases) {\n\n            if (isSameEntry(entryView, otherView)) {\n                continue;\n            }\n\n            Range otherRange = VisualBoundsResolver.getRange(otherView, dayView, contentWidth);\n\n            if (entry.isFullDay()) {\n                otherRange.y1 = 0;\n                otherRange.y2 = dayView.getHeight();\n            }\n\n            if (entryRange.y1 < otherRange.y2 && entryRange.y2 > otherRange.y1) {\n\n                /*\n                 * The two activities intersect, so we can not use this column\n                 * for the passed activity.\n                 */\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private boolean isSameEntry(EntryViewBase<?> viewA, EntryViewBase<?> viewB) {\n        Entry<?> entryA = viewA.getEntry();\n        Entry<?> entryB = viewB.getEntry();\n\n        if (entryA instanceof DraggedEntry) {\n            return isSameEntry((DraggedEntry) entryA, entryB);\n        }\n\n        if (entryB instanceof DraggedEntry) {\n            return isSameEntry((DraggedEntry) entryB, entryA);\n        }\n\n        return false;\n    }\n\n    private boolean isSameEntry(DraggedEntry draggedEntry, Entry<?> entry) {\n        if (entry.isRecurrence()) {\n            return draggedEntry.getOriginalEntry().getRecurrenceSourceEntry() == entry.getRecurrenceSourceEntry();\n        }\n\n        return draggedEntry.getOriginalEntry() == entry;\n    }\n\n    public List<EntryViewBase<?>> getEntryViews() {\n        return entryViewBases;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/impl/com/calendarfx/view/util/VisualBoundsResolver.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage impl.com.calendarfx.view.util;\n\nimport com.calendarfx.model.Entry;\nimport com.calendarfx.view.DayView;\nimport com.calendarfx.view.EntryViewBase;\nimport com.calendarfx.view.EntryViewBase.HeightLayoutStrategy;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\n@SuppressWarnings(\"javadoc\")\npublic final class VisualBoundsResolver {\n\n    static class Range {\n        String title;\n        double y1;\n        double y2;\n\n        @Override\n        public String toString() {\n            return \"title = \" + title + \", y1 = \" + y1 + \", y2 = \" + y2;\n        }\n    }\n\n    public static Range getRange(EntryViewBase entryView, DayView dayView, double contentWidth) {\n        Entry<?> entry = entryView.getEntry();\n\n        Range range = new Range();\n\n        range.y1 = dayView.getLocation(entry.getStartAsZonedDateTime());\n        range.y2 = dayView.getLocation(entry.getEndAsZonedDateTime());\n\n        if (entryView.getHeightLayoutStrategy().equals(HeightLayoutStrategy.COMPUTE_PREF_SIZE)) {\n            range.y2 = range.y1 + entryView.prefHeight(contentWidth);\n        }\n\n        range.title = entry.getTitle();\n        return range;\n    }\n\n\n    private static Comparator<EntryViewBase<?>> additionalComparator;\n\n    /**\n     * The resolver always sorts entries based on their visual bounds but when\n     * entries have the same bounds the application might want to sort them based on\n     * additional criteria. This can be implemented this way.\n     *\n     * @return\n     */\n    public static Comparator<EntryViewBase<?>> getAdditionalComparator() {\n        return additionalComparator;\n    }\n\n    public static void setAdditionalComparator(Comparator<EntryViewBase<?>> additionalComparator) {\n        VisualBoundsResolver.additionalComparator = additionalComparator;\n    }\n\n    public static <T extends EntryViewBase> List<Placement> resolve(List<T> entryViews, DayView dayView, double contentWidth) {\n        final Comparator<T> comparator = (o1, o2) -> {\n\n            final Range range1 = getRange(o1, dayView, contentWidth);\n            final Range range2 = getRange(o2, dayView, contentWidth);\n\n            if (range1.y1 < range2.y1) {\n                return -1;\n            } else if (range1.y1 > range2.y1) {\n                return +1;\n            }\n\n            if (additionalComparator != null) {\n                return additionalComparator.compare(o1, o2);\n            }\n\n            return 0;\n        };\n\n        Collections.sort(entryViews, comparator);\n\n        List<Placement> placements = new ArrayList<>();\n        List<VisualBoundsCluster> clusters = new ArrayList<>();\n\n        VisualBoundsCluster cluster = null;\n\n        for (T entryView : entryViews) {\n            if (entryView.isVisible()) {\n                if (cluster == null || !cluster.intersects(entryView, dayView, contentWidth)) {\n                    cluster = new VisualBoundsCluster();\n                    clusters.add(cluster);\n                }\n\n                cluster.add(entryView, dayView, contentWidth);\n            }\n        }\n\n        for (VisualBoundsCluster c : new ArrayList<>(clusters)) {\n            placements.addAll(c.resolve(dayView, contentWidth));\n        }\n\n        return placements;\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/main/java/module-info.java",
    "content": "module com.calendarfx.view {\n\n    requires java.logging;\n\n    requires transitive javafx.controls;\n    requires transitive org.controlsfx.controls;\n    requires transitive org.kordamp.ikonli.javafx;\n    requires transitive org.kordamp.ikonli.fontawesome;\n    requires java.desktop;\n    requires ical4j.core;\n    requires com.dlsc.gemsfx;\n\n    exports com.calendarfx.model;\n    exports com.calendarfx.util;\n    exports com.calendarfx.view;\n    exports com.calendarfx.view.page;\n    exports com.calendarfx.view.popover;\n    exports com.calendarfx.view.print;\n\n    exports impl.com.calendarfx.view;\n    exports impl.com.calendarfx.view.page;\n    exports impl.com.calendarfx.view.popover;\n    exports impl.com.calendarfx.view.print;\n    exports impl.com.calendarfx.view.util;\n\n    opens com.calendarfx.view;\n    opens com.calendarfx.model;\n    opens impl.com.calendarfx.view;\n}"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/util/public_key.properties",
    "content": "public=308201B73082012C06072A8648CE3804013082011F02818100FD7F53811D75122952DF4A9C2EECE4E7F611B7523CEF4400C31E3F80B6512669455D402251FB593D8D58FABFC5F5BA30F6CB9B556CD7813B801D346FF26660B76B9950A5A49F9FE8047B1022C24FBBA9D7FEB7C61BF83B57E7C6A8A6150F04FB83F6D3C51EC3023554135A169132F675F3AE2B61D72AEFF22203199DD14801C70215009760508F15230BCCB292B982A2EB840BF0581CF502818100F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A03818400028180279C65FB46B56A344EC63EDC723BA9D8A3DAAECF55D40D0406A5DA7E0B4F88A70ECCCFD8CF9648E427C49FBFCD24E1CCD6A04CECF4B2FA795829524D9F10106D76E0BDF11698A125DBBA06AA3D1E1AFDC2CF7D9D387098E5AFAC89C5512CADD5A78A7DD12E3AA6B4CABA13257ED051630ACEAA40A7F3DAAC318FE6C0943FE81D"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/util/version.properties",
    "content": "calendarfx.version = ${project.version}"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/atlantafx.css",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Base colors for the predefined styles\n */\n\n* {\n    -fx-font-family: \"Arial\";\n\n    -style1-color: -color-chart-1;\n    -style2-color: -color-chart-2;\n    -style3-color: -color-chart-3;\n    -style4-color: -color-chart-4;\n    -style5-color: -color-chart-5;\n    -style6-color: -color-chart-6;\n    -style7-color: -color-chart-7;\n\n    -weekend-fill-color: -color-bg-subtle;\n    -today-color: -color-danger-emphasis;\n    -today-background-color: -color-bg-subtle;\n    -weekend-background-color: -color-bg-subtle;\n\n    -usage-very-low-color: lightgreen;\n    -usage-low-color: green;\n    -usage-medium-color: gold;\n    -usage-high-color: darkorange;\n    -usage-very-high-color: red;\n\n    -usage-very-low-text-color: green;\n    -usage-low-text-color: lightgreen;\n    -usage-medium-text-color: black;\n    -usage-high-text-color: black;\n    -usage-very-high-text-color: white;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * A few ID-based styles.\n */\n\n#toolbar {\n    -fx-padding: 10px;\n    -fx-border-width: 0px 0px 1px 0px;\n    -fx-border-color: -color-border-default;\n}\n\n#switcher {\n\n}\n\n#search-field {\n    -fx-background-radius: 100.0px;\n    -fx-pref-width: 200.0px;\n}\n\n#search-icon {\n    -glyph-name: \"SEARCH\";\n    -glyph-size: 1.0em;\n}\n\n#date-picker {\n    -fx-border-color: -color-border-default transparent transparent transparent;\n    -fx-border-insets: 0px 6.0 0px 6.0;\n    -fx-background-color: -color-bg-subtle;\n    -fx-min-width: 200px;\n    -fx-min-height: 180px;\n}\n\n#tray-pane {\n    -fx-border-color: null;\n}\n\n.date-hyperlink {\n    -fx-cursor: hand;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for CalendarView.\n */\n\n.calendar-view .master-detail-pane > .split-pane > .split-pane-divider {\n    -fx-padding: 0px 1px 0px 0px;\n}\n\n.calendar-view .master-detail-pane > .split-pane {\n    -fx-background-color: transparent;\n    -fx-background-insets: 0px, 0px;\n    -fx-padding: 0px;\n}\n\n.calendar-view .button-icon {\n    -fx-icon-size: 14.0px;\n}\n\n.right-toolbar-container {\n    -fx-spacing: 10px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PageBase abstract superclass of the day, week, month, and year page.\n * The page base consists of a big content node in the middle and a header that displays\n * navigation buttons to go forward, back, to today and the currently shown date (range).\n */\n\n.calendar-page {\n    -fx-background-color: -color-bg-default;\n}\n\n.calendar-page > .container > .content {\n    -fx-padding: 10px;\n}\n\n.calendar-page > .container > .header {\n}\n\n.calendar-page > .container > .header > .date-text {\n    -fx-font-size: 2.2em;\n}\n\n.calendar-page > .container > .header > .navigate-date-view {\n}\n\n.calendar-page > .container > .header > .navigate-date-view > .container > .previous-date-button {\n    -fx-padding: 3 7px 3 7px;\n}\n\n.calendar-page > .container > .header > .navigate-date-view > .container > .next-date-button {\n    -fx-padding: 3 7px 3 7px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the \"days\" page.\n */\n\n.day-page > .container > .content > .left-side > .header {\n    -fx-background-color: -color-bg-subtle;\n    -fx-background-radius: 4px;\n    -fx-padding: 0 1em 0 1em;\n}\n\n.day-page > .container > .content > .left-side > .header > .today-label {\n    -fx-font-size: 8.0em;\n    -fx-text-fill: -color-fg-default;\n    -fx-opacity: 0.9;\n}\n\n.day-page > .container > .content > .left-side > .agenda-view {\n    -fx-padding: 2em 0 0 0;\n    -fx-background-insets: 0px;\n}\n\n.day-page .day-view {\n    -fx-pref-width: 350px;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-week-label {\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-padding: 0px 0px 5px 0px;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .week-label,\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-week-label {\n    -fx-font-size: 10px;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .week-label {\n    -fx-padding: 0px 8.0 0px 0px;\n    -fx-min-width: 2.0em;\n    -fx-min-height: 2.0em;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .current-date-label {\n    -fx-text-fill: -today-color;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .current-date-border {\n    -fx-border-color: transparent transparent -today-color transparent;\n    -fx-border-width: 2.0;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .month-day {\n    -fx-padding: .2em .3em .2em .3em;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-month-label {\n    -fx-text-fill: -color-fg-default;\n    -fx-font-size: .9em;\n    -fx-opacity: 0.8;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-not-of-month-label {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .weekend-day {\n    -fx-background-color: transparent;\n}\n\n.toolbar-controls-box {\n    -fx-spacing: 5px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the DatePopOver control.\n */\n\n.date-popover > .border {\n    -fx-fill: -color-border-default;\n}\n\n.date-popover > .content {\n    -fx-background-color: -color-bg-default;\n}\n\n.date-popover > .content > .entries-pane {\n    -fx-padding: 0.5em;\n}\n\n.date-popover > .content > .entries-pane > .entry > .title {\n    -fx-padding: 2.0;\n    -fx-font-size: 12px;\n}\n\n.date-popover > .content > .no-entries-label {\n    -fx-padding: 4.0;\n    -fx-font-size: 14px;\n}\n\n.date-popover > .content > .entries-pane > .entry > .time {\n    -fx-padding: 2.0 2.0 2.0 40.0;\n    -fx-font-size: 10px;\n    -fx-text-fill: -color-fg-muted;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for the EntryDetailsView.\n */\n\n.entry-details-view {\n    -fx-background-color: -color-bg-subtle !important;\n}\n\n.entry-details-view > .content {\n    -fx-background-color: -color-bg-subtle;\n    -fx-hgap: 1.0em;\n    -fx-vgap: 1.0em;\n}\n\n.entry-details-view > .content > .recurrence-summary-label {\n    -fx-font-weight: bold;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the source view. This control displays the currently known\n * calendar sources and for each source the calendars inside of it.\n */\n\n.source-view > .container > .titled-pane > .title > .arrow-button .arrow {\n    -fx-opacity: 0;\n}\n\n.source-view > .container > .titled-pane > .title > .arrow-button {\n    -fx-min-width: 0px;\n    -fx-min-height: 0px;\n    -fx-pref-width: 0px;\n    -fx-pref-height: 0px;\n    -fx-padding: 0px;\n}\n\n.source-view > .container > .titled-pane {\n    -fx-background-color: transparent;\n    -fx-animated: true;\n}\n\n.source-view > .container > .titled-pane:expanded {\n}\n\n.source-view > .container > .titled-pane > .title {\n    -fx-font-weight: bold;\n    -fx-background-color: transparent;\n    -fx-border-color: transparent transparent transparent transparent;\n    -fx-border-insets: 1px 10px 1px 10px;\n}\n\n.source-view > .container > .titled-pane:collapsed > .title {\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-effect: null;\n}\n\n.source-view > .container > .titled-pane > .content {\n    -fx-border-color: transparent;\n    -fx-padding: 0px 20px 0px 20px;\n    -fx-background-color: transparent;\n}\n\n.source-view > .container > .titled-pane > .title > .arrow-button > .arrow {\n    -fx-min-width: 0px;\n    -fx-min-height: 0px;\n    -fx-pref-width: 0px;\n    -fx-pref-height: 0px;\n    -fx-padding: 0px;\n}\n\n.source-view > .container > .single-calendar-group {\n    -fx-padding: 1em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the scroll pane that wraps the source view.\n */\n\n.source-view-scroll-pane,\n.source-view-scroll-pane:focused {\n    -fx-padding: 0px;\n    -fx-background-radius: 0px;\n    -fx-background-insets: 0px;\n    -fx-background-color: transparent;\n    -fx-border-radius: 0px;\n    -fx-border-insets: 0px;\n    -fx-border-color: transparent;\n    -fx-fit-to-width: true;\n    -fx-fit-to-height: true;\n}\n\n.source-view-scroll-pane .corner {\n    -fx-background-insets: 0.0px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the year view.\n */\n\n.year-view .scroll-pane {\n    -fx-padding: 0px;\n    -fx-background-insets: 0px;\n}\n\n.year-view .scroll-pane .container {\n    -fx-background-color: -color-bg-default;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the year month view.\n */\n.year-month-view > .container > .header {\n    -fx-padding: 0px 0px 10px 0px;\n}\n\n.year-month-view > .container > .header > .month-header {\n}\n\n.year-month-view > .container > .header > .year-header {\n}\n\n.year-month-view > .container > .header > .month-header > .month-label {\n    -fx-font-size: 18px;\n}\n\n.year-month-view > .container > .header > .year-header > .year-label {\n    -fx-font-size: 18px;\n}\n\n.year-month-view > .container {\n    -fx-padding: 1em;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button,\n.year-month-view > .container > .header > .year-header > .next-button,\n.year-month-view > .container > .header > .month-header > .previous-button,\n.year-month-view > .container > .header > .month-header > .next-button {\n    -fx-background-color: -color-neutral-emphasis;\n    -fx-pref-width: 8.0;\n    -fx-pref-height: 8.0;\n    -fx-max-width: 8.0;\n    -fx-max-height: 8.0;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button:hover,\n.year-month-view > .container > .header > .year-header > .next-button:hover,\n.year-month-view > .container > .header > .month-header > .previous-button:hover,\n.year-month-view > .container > .header > .month-header > .next-button:hover {\n    -fx-background-color: -color-neutral-emphasis-plus;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button:pressed,\n.year-month-view > .container > .header > .year-header > .next-button:pressed,\n.year-month-view > .container > .header > .month-header > .previous-button:pressed,\n.year-month-view > .container > .header > .month-header > .next-button:pressed {\n    -fx-background-color: -color-neutral-emphasis-muted;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button,\n.year-month-view > .container > .header > .month-header > .previous-button {\n    -fx-shape: \"M3,0 V3 L0 1.5 L3,0 Z\";\n}\n\n.year-month-view > .container > .header > .year-header > .next-button,\n.year-month-view > .container > .header > .month-header > .next-button {\n    -fx-shape: \"M0,0 L3 1.5 L0,3 V0 Z\";\n}\n\n.year-month-view > .container > .day-of-week-label {\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-padding: 0px 0px 5px 0px;\n}\n\n.year-month-view > .container > .week-label,\n.year-month-view > .container > .day-of-week-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: 10px;\n}\n\n.year-month-view > .container > .week-label {\n    -fx-padding: 0px 2.0 0px 0px;\n    -fx-min-width: 2.0em;\n    -fx-min-height: 2.0em;\n}\n\n.year-month-view > .container > .current-date-label {\n    -fx-text-fill: -today-color;\n}\n\n.year-month-view > .container > .current-date-border {\n    -fx-border-color: transparent transparent -today-color transparent;\n    -fx-border-width: 2.0;\n}\n\n.year-month-view > .container > .month-day {\n    -size: 2em;\n    -fx-pref-width: -size;\n    -fx-pref-height: -size;\n    -fx-min-width: -size;\n    -fx-min-height: -size;\n}\n\n.year-month-view > .container > .day-of-month-label {\n    -fx-text-fill: -color-fg-default;\n}\n\n.year-month-view > .container > .day-not-of-month-label {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.year-month-view > .container > .weekend-day {\n    -fx-background-color: -color-bg-subtle;\n}\n\n.year-month-view > .container > .usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.year-month-view > .container > .usage-low {\n    -fx-background-color: -usage-low-color;\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.year-month-view > .container > .usage-medium {\n    -fx-background-color: -usage-medium-color;\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.year-month-view > .container > .usage-high {\n    -fx-background-color: -usage-high-color;\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.year-month-view > .container > .usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.year-month-view > .container > .selected-month-date {\n    -fx-background-color: lightgray;\n    -fx-text-fill: black;\n    -fx-font-weight: bold;\n    -fx-background-radius: 10em;\n}\n\n.year-month-view > .container > .today {\n    -fx-background-color: -today-color;\n    -fx-text-fill: -color-bg-default;\n    -fx-font-weight: bold;\n    -fx-background-radius: 10em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the month view.\n */\n\n.month-view > .container > .day {\n    -fx-padding: 0.0px;\n    -fx-background-insets: 0.0px;\n    -fx-border-color: -color-border-default transparent transparent transparent;\n    -fx-border-insets: 0px;\n    -fx-border-width: 1px 0px 0px 0px;\n}\n\n.month-view > .container > .day:selected {\n    -fx-background-color: -color-accent-emphasis;\n}\n\n.month-view > .container > .today,\n.month-view > .container > .current-week {\n    -fx-border-insets: 0px;\n    -fx-border-width: 5px 0px 0px 0px;\n}\n\n.month-view > .container > .current-week {\n    -fx-border-color: -today-color transparent transparent transparent;\n}\n\n.month-view > .container > .today {\n    -fx-border-color: -today-color transparent transparent transparent;\n}\n\n.month-view > .container > .today:selected {\n    -fx-border-color: -today-color transparent transparent transparent;\n}\n\n.month-view > .container > .day:selected > .header > .day-of-month-label,\n.month-view > .container > .day:selected > .header > .day-not-of-month-label {\n    -fx-text-fill: -color-fg-emphasis;\n}\n\n.month-view > .container > .day > .entries-pane {\n    -fx-spacing: 1.0px;\n}\n\n.month-view > .container > .day > .header {\n    -fx-padding: 2.0 4.0 2.0 0px;\n}\n\n.month-view > .container > .day > .header > .day-of-month-label,\n.month-view > .container > .day > .header > .day-not-of-month-label {\n    -fx-alignment: center-right;\n    -fx-font-weight: bold;\n}\n\n.month-view > .container > .day > .header > .day-of-month-label {\n    -fx-text-fill: -color-fg-default;\n}\n\n.month-view > .container > .day > .header > .day-not-of-month-label {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.month-view > .container > .day > .header > .weekend-day {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.month-view > .container > .day > .header > .week-of-year-label,\n.month-view > .container > .day > .header > .current-week-of-year-label {\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-padding: 0.25em;\n}\n\n.month-view > .container > .day > .header > .week-of-year-label {\n    -fx-text-fill: -color-fg-emphasis;\n    -fx-background-color: -color-neutral-emphasis;\n}\n\n.month-view > .container > .day > .header > .current-week-of-year-label {\n    -fx-text-fill: -today-color;\n    -fx-background-color: -today-background-color;\n}\n\n.month-view > .container > .day-of-week-label {\n    -fx-text-fill: -color-fg-default;\n    -fx-padding: 0px 0px 5px 0px;\n    -fx-font-size: 1.1em;\n    -fx-font-weight: bold;\n}\n\n.month-view > .container > .day-of-weekend-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-padding: 0px 0px 5px 0px;\n    -fx-font-size: 1.1em;\n    -fx-font-weight: bold;\n}\n\n.month-view > .container > .weekend-day {\n    -fx-background-color: -weekend-background-color;\n}\n\n.month-view > .container > .day > .entries-pane > .more-label {\n    -fx-text-fill: gray;\n    -fx-font-weight: bold;\n    -fx-padding: 0px 0px 0px 10px;\n}\n\n.month-view > .container > .day > .header > .today-label {\n    -fx-text-fill: -today-color;\n    -fx-background-insets: 0px 4px 0px 4px;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the week day separators.\n */\n.week-view .weekday-separator,\n.weekday-header-view .weekday-separator,\n.all-day-view .weekday-separator {\n    -size: 0px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: -color-border-default;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the day view.\n */\n\n.day-view {\n    -fx-padding: 0px 0px 0px 0px;\n}\n\n.day-view.today {\n}\n\n.day-view.weekend-day {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n.day-view > .half-hour-line {\n    -fx-stroke: -color-border-default;\n    -fx-opacity: 0.5;\n}\n\n.day-view > .full-hour-line {\n    -fx-stroke: -color-border-default;\n}\n\n.day-view > .midnight-line {\n    -fx-stroke-width: 2;\n    -fx-stroke: darkgrey;\n}\n\n.day-view > .noon-line {\n    -fx-stroke-width: 2;\n    -fx-stroke: -color-neutral-emphasis-plus;\n    -fx-stroke-dash-array: 6 6;\n}\n\n.day-view > .current-time-line {\n    -fx-stroke: -today-color;\n}\n\n.day-view > .early-hour-line,\n.day-view > .late-hour-line {\n    -fx-opacity: 0.5;\n}\n\n.day-view > .early-hours-region,\n.day-view > .late-hours-region {\n    -fx-background-color: rgba(200, 200, 200, 0.2);\n}\n\n.day-view > .current-time-circle {\n    -fx-stroke: -today-color;\n    -fx-fill: -today-color;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the weekday view.\n */\n\n.weekday-view {\n}\n\n.weekday-view.today {\n    -fx-background-color: -today-background-color;\n}\n\n.weekday-view.weekend-day {\n    -fx-background-color: -weekend-background-color;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the time scale (01:00, 02:00, .... 24:00).\n */\n\n.time-scale {\n    -fx-padding: 0px 5px 0px 5px;\n}\n\n.time-scale > .time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -color-fg-muted;\n}\n\n.time-scale > .date-label {\n    -fx-font-size: 0.8em;\n    -fx-background-color: -color-neutral-emphasis;\n    -fx-background-radius: 2px;\n    -fx-padding: 2px 4px;\n    -fx-text-fill: -color-fg-emphasis;\n}\n\n.time-scale > .current-time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -today-color;\n}\n\n.time-scale > .early-hour-label,\n.time-scale > .late-hour-label {\n    -fx-opacity: 0.5;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the WeekDayHeaderView.\n */\n\n.weekday-header-view > .container > .cell {\n    -fx-font-size: 1.1em;\n    -fx-font-weight: bold;\n    -fx-padding: 0px 0px 4.0 0px;\n    -fx-border-insets: 0px 0px 0px 0px;\n    -fx-alignment: center;\n    -fx-border-color: transparent transparent -color-border-default transparent;\n}\n\n.weekday-header-view > .container > .today {\n    -fx-text-fill: -today-color;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the AllDayView.\n */\n\n.all-day-view {\n    -fx-min-height: 2em;\n    -fx-row-height: 20px; /* custom styleable property */\n    -fx-row-spacing: 2px; /* custom styleable property */\n    -fx-column-spacing: 2px; /* custom styleable property */\n}\n\n.all-day-view > .container > .day-region {\n    -fx-border-width: 0px 0px 5px 0px;\n    -fx-border-color: transparent transparent -color-border-default transparent;\n}\n\n.all-day-view > .container > .today {\n    -fx-border-color: transparent transparent -today-color transparent;\n    -fx-background-color: -today-background-color;\n}\n\n.all-day-view > .container > .weekend {\n    -fx-background-color: -weekend-background-color;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the AgendaView.\n */\n\n.agenda-view > .container > .list-view > .placeholder-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: 2.5em;\n}\n\n.agenda-view > .container {\n    -fx-padding: 0px;\n}\n\n.agenda-view > .container > .list-view {\n    -fx-background-color: transparent;\n}\n\n.agenda-view > .container > .status-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-alignment: center;\n}\n\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical,\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical .decrement-arrow,\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical .increment-arrow {\n    -fx-pref-width: 0px;\n}\n\n.agenda-view-list-cell {\n    -fx-background-color: transparent;\n}\n\n.agenda-view-list-cell > .container > .header {\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-border-width: 3.0;\n    -fx-border-insets: 0px 0px 4.0 0px;\n}\n\n.agenda-view-list-cell > .container > .header.today {\n    -fx-border-color: transparent transparent -today-color transparent;\n}\n\n.agenda-view-list-cell > .container > .header > .date-label,\n.agenda-view-list-cell > .container > .header > .weekday-label {\n    -fx-padding: 0px 0px 4px 0px;\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-text-fill: -color-fg-muted;\n}\n\n.agenda-view-list-cell > .container > .header > .today,\n.agenda-view-list-cell > .container > .header > .today {\n    -fx-text-fill: -today-color;\n}\n\n.agenda-view-list-cell > .container > .body {\n    -fx-padding: 0px 0px 20px 0px;\n    -fx-vgap: 5px;\n    -fx-hgap: 8px;\n}\n\n.agenda-view-list-cell > .container > .body > .title-label {\n    -fx-padding: 4px 4px 4px 0px;\n    -fx-text-fill: -color-fg-default;\n    -fx-font-weight: bold;\n}\n\n.agenda-view-list-cell > .container > .body > .time-label {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.agenda-view-list-cell > .container > .body > .separator {\n    -fx-pref-height: 1px;\n    -fx-border-color: -color-border-default;\n    -fx-border-style: dashed none none none;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the SearchResultView.\n */\n\n.search-result-view > .list-view,\n.search-result-view > .list-view:focused {\n    -fx-pref-width: 200px;\n    -fx-background-color: -color-bg-subtle;\n}\n\n.search-result-view > .list-view > .placeholder-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: 1.5em;\n}\n\n.search-result-view > .list-view .search-result-cell:selected {\n    -fx-background-color: -color-accent-emphasis;\n}\n\n.search-result-view > .list-view .search-result-cell:filled:focused:selected {\n}\n\n.search-result-view > .list-view .search-result-cell {\n    -fx-background-color: transparent;\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-border-width: 0px 0px 1px 0px;\n    -fx-border-insets: 0px 6px 0px 6px;\n    -fx-padding: 5px;\n}\n\n.search-result-view > .list-view .search-result-cell > .container > .title-label {\n    -fx-font-weight: bold;\n    -fx-font-size: 0.9em;\n    -fx-text-fill: -color-fg-default;\n}\n\n.search-result-view > .list-view .search-result-cell > .container > .date-time-pane > .date-label,\n.search-result-view > .list-view .search-result-cell > .container > .date-time-pane > .time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -color-fg-muted;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the MonthEntryView.\n */\n\n.month-entry-view {\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Default styles for calendar entries.\n */\n\n.default-style-entry {\n    -fx-background-color: -color-neutral-emphasis;\n    -fx-background-radius: 0px;\n    -fx-background-insets: 0px 0px 0px 2px;\n    -fx-border-insets: 0px 0px 0px 2px;\n    -fx-border-width: 0px 0px 0px 4px;\n}\n\n.default-style-entry:dragged {\n    -fx-opacity: 0.5;\n}\n\n.default-style-entry:dragged-start,\n.default-style-entry:dragged-end {\n    -fx-opacity: 0px;\n}\n\n.default-style-entry:selected {\n    -fx-border-width: 0px;\n}\n\n.default-style-entry .icon-flow-pane {\n    -fx-hgap: 0px;\n    -fx-vgap: 0px;\n}\n\n.default-style-entry-time-label {\n    -fx-font-size: 0.9em;\n    -fx-padding: 4px 4px 2px 4px;\n}\n\n.default-style-entry-title-label {\n    -fx-font-size: 0.9em;\n    -fx-font-weight: bold;\n    -fx-padding: 0px 4px 0px 4px;\n}\n\n.default-style-entry-title-label-full-day {\n    -fx-font-size: 0.9em;\n    -fx-font-weight: bold;\n    -fx-padding: 0px 4px 0px 4px;\n}\n\n.default-style-entry:selected .default-style-entry-time-label {\n    -fx-text-fill: white;\n}\n\n.default-style-entry:selected .default-style-entry-title-label {\n    -fx-text-fill: white;\n}\n\n.default-style-visibility-checkbox > .box > .mark {\n    -fx-background-color: transparent;\n}\n\n.default-style-visibility-checkbox:selected > .box > .mark {\n    -fx-background-color: -color-dark;\n    -fx-opacity: .6;\n}\n\n.default-style-visibility-checkbox:focused > .box {\n}\n\n.default-style-entry-popover-title {\n}\n\n/* Small entries */\n.default-style-entry-small {\n    -fx-padding: 0.15em 0.5em 0.15em 0.5em;\n    -fx-font-size: 0.9em;\n}\n\n.default-style-entry-small-only, .default-style-entry-small-last {\n    -fx-background-insets: 0px 2px 0px 0px;\n}\n\n.default-style-entry-small > .default-style-entry-small-title-label {\n}\n\n.all-day-view .default-style-entry-small > .default-style-entry-small-title-label {\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small > .default-style-entry-small-time-label {\n}\n\n.default-style-entry-small:selected > .default-style-entry-small-title-label {\n    -fx-text-fill: -color-light;\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small:selected > .default-style-entry-small-time-label {\n    -fx-text-fill: -color-light;\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small:selected > * > .default-style-icon,\n.default-style-entry-small:selected > * > .default-style-icon-small {\n    -fx-stroke: -color-light;\n    -fx-fill: -color-light;\n}\n\n.default-style-entry-small-time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -color-light;\n    -fx-opacity: .8;\n}\n\n.default-style-entry-small-time-label-full-day {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -color-light\n}\n\n.default-style-entry-small-title-label {\n    -fx-text-fill: -color-light\n}\n\n.default-style-entry-small-title-label-full-day {\n    -fx-text-fill: -color-light\n}\n\n.default-style-entry-small-full-day {\n}\n\n.default-style-entry-small-full-day:selected > .default-style-entry-small-time-label {\n    -fx-text-fill: -color-light;\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small-full-day:selected > .default-style-entry-small-title-label {\n    -fx-text-fill: -color-light;\n    -fx-font-weight: bold;\n}\n\n.default-style-calendar-header {\n    -fx-font-weight: bold;\n    -fx-background-color: magenta;\n    -fx-alignment: center;\n    -fx-padding: 0.25em;\n    -fx-text-overrun: clip;\n}\n\n.default-style-calendar-header:first,\n.default-style-calendar-header:middle,\n.default-style-calendar-header:last {\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 1 (green).\n */\n\n.style1-icon, .style1-icon-small {\n    -fx-fill: -style1-color;\n    -fx-stroke: -style1-color;\n}\n\n.style1-entry {\n    -fx-background-color: derive(-style1-color, 70%);\n    -fx-border-color: -style1-color;\n}\n\n.style1-entry:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-entry-time-label, .style1-entry-title-label {\n    -fx-text-fill: derive(-style1-color, -30%);\n}\n\n.style1-entry-small:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-entry-small-full-day {\n    -fx-background-color: derive(-style1-color, 70%);\n}\n\n.style1-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style1-color, -30%);\n}\n\n.style1-entry-small-full-day:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-visibility-checkbox > .box,\n.style1-source-grid-item-box {\n    -fx-background-color: derive(-style1-color, 70%);\n}\n\n.style1-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style1-color, 70%);\n}\n\n.style1-entry-popover-title {\n    -fx-text-fill: derive(-style1-color, -50%);\n}\n\n.style1-calendar-header {\n    -fx-background-color: derive(-style1-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 2 (blue).\n */\n\n.style2-icon, .style2-icon-small {\n    -fx-fill: -style2-color;\n    -fx-stroke: -style2-color;\n}\n\n.style2-entry {\n    -fx-background-color: derive(-style2-color, 70%);\n    -fx-border-color: -style2-color;\n}\n\n.style2-entry:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-entry-time-label, .style2-entry-title-label {\n    -fx-text-fill: derive(-style2-color, -50%);\n}\n\n.style2-entry-small:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-entry-small-full-day {\n    -fx-background-color: derive(-style2-color, 70%);\n}\n\n.style2-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style2-color, -30%);\n}\n\n.style2-entry-small-full-day:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-visibility-checkbox > .box,\n.style2-source-grid-item-box {\n    -fx-background-color: derive(-style2-color, 70%);\n}\n\n.style2-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style2-color, 70%);\n}\n\n.style2-entry-popover-title {\n    -fx-text-fill: derive(-style2-color, -30%);\n}\n\n.style2-calendar-header {\n    -fx-background-color: derive(-style2-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 3 (yellow).\n */\n\n.style3-icon, .style3-icon-small {\n    -fx-fill: -style3-color;\n    -fx-stroke: -style3-color;\n}\n\n.style3-entry {\n    -fx-background-color: derive(-style3-color, 70%);\n    -fx-border-color: -style3-color;\n}\n\n.style3-entry:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-entry-time-label, .style3-entry-title-label {\n    -fx-text-fill: derive(-style3-color, -50%);\n}\n\n.style3-entry-small:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-entry-small-full-day {\n    -fx-background-color: derive(-style3-color, 70%);\n}\n\n.style3-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style3-color, -30%);\n}\n\n.style3-entry-small-full-day:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-visibility-checkbox > .box,\n.style3-source-grid-item-box {\n    -fx-background-color: derive(-style3-color, 70%);\n}\n\n.style3-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style3-color, 70%);\n}\n\n.style3-entry-popover-title {\n    -fx-text-fill: derive(-style3-color, -30%);\n}\n\n.style3-calendar-header {\n    -fx-background-color: derive(-style3-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 4 (purple).\n */\n\n.style4-icon, .style4-icon-small {\n    -fx-fill: -style4-color;\n    -fx-stroke: -style4-color;\n}\n\n.style4-entry {\n    -fx-background-color: derive(-style4-color, 70%);\n    -fx-border-color: -style4-color;\n}\n\n.style4-entry:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-entry-time-label, .style4-entry-title-label {\n    -fx-text-fill: derive(-style4-color, -50%);\n}\n\n.style4-entry-small:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-entry-small-full-day {\n    -fx-background-color: derive(-style4-color, 70%);\n}\n\n.style4-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style4-color, -30%);\n}\n\n.style4-entry-small-full-day:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-visibility-checkbox > .box,\n.style4-source-grid-item-box {\n    -fx-background-color: derive(-style4-color, 70%);\n}\n\n.style4-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style4-color, 70%);\n}\n\n.style4-entry-popover-title {\n    -fx-text-fill: derive(-style4-color, -30%);\n}\n\n.style4-calendar-header {\n    -fx-background-color: derive(-style4-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 5 (red).\n */\n\n.style5-icon, .style5-icon-small {\n    -fx-fill: -style5-color;\n    -fx-stroke: -style5-color;\n}\n\n.style5-entry {\n    -fx-background-color: derive(-style5-color, 70%);\n    -fx-border-color: -style5-color;\n}\n\n.style5-entry:selected {\n    -fx-background-color: -style5-color;\n}\n\n.style5-entry-time-label, .style5-entry-title-label {\n    -fx-text-fill: derive(-style5-color, -50%);\n}\n\n.style5-entry-small:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style5-entry-small-full-day {\n    -fx-background-color: derive(-style5-color, 70%);\n}\n\n.style5-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style5-color, -30%);\n}\n\n.style5-entry-small-full-day:selected {\n    -fx-background-color: -style5-color;\n}\n\n.style5-visibility-checkbox > .box {\n    -fx-background-color: derive(-style5-color, 70%);\n}\n\n.style5-visibility-checkbox:focused > .box,\n.style5-source-grid-item-box {\n    -fx-background-color: derive(-style5-color, 70%);\n}\n\n.style5-entry-popover-title {\n    -fx-text-fill: derive(-style5-color, -30%);\n}\n\n.style5-calendar-header {\n    -fx-background-color: derive(-style5-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 6 (orange).\n */\n\n.style6-icon, .style6-icon-small {\n    -fx-fill: -style6-color;\n    -fx-stroke: -style6-color;\n}\n\n.style6-entry {\n    -fx-background-color: derive(-style6-color, 70%);\n    -fx-border-color: -style6-color;\n}\n\n.style6-entry:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-entry-time-label, .style6-entry-title-label {\n    -fx-text-fill: derive(-style6-color, -50%);\n}\n\n.style6-entry-small:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-entry-small-full-day {\n    -fx-background-color: derive(-style6-color, 70%);\n}\n\n.style6-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style6-color, -30%);\n}\n\n.style6-entry-small-full-day:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-visibility-checkbox > .box,\n.style6-source-grid-item-box {\n    -fx-background-color: derive(-style6-color, 70%);\n}\n\n.style6-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style6-color, 70%);\n}\n\n.style6-entry-popover-title {\n    -fx-text-fill: derive(-style6-color, -30%);\n}\n\n.style6-calendar-header {\n    -fx-background-color: derive(-style6-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 7 (brown).\n */\n\n.style7-icon, .style7-icon-small {\n    -fx-fill: -style7-color;\n    -fx-stroke: -style7-color;\n}\n\n.style7-entry {\n    -fx-background-color: derive(-style7-color, 70%);\n    -fx-border-color: -style7-color;\n}\n\n.style7-entry:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-entry-time-label, .style7-entry-title-label {\n    -fx-text-fill: derive(-style7-color, -50%);\n}\n\n.style7-entry-small:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-entry-small-full-day {\n    -fx-background-color: derive(-style7-color, 70%);\n}\n\n.style7-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style7-color, -30%);\n}\n\n.style7-entry-small-full-day:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-visibility-checkbox > .box,\n.style7-source-grid-item-box {\n    -fx-background-color: derive(-style7-color, 70%);\n}\n\n.style7-visibility-checkbox:focused > .box {\n    -fx-background-color: derive(-style7-color, 70%);\n}\n\n.style7-entry-popover-title {\n    -fx-text-fill: derive(-style7-color, -30%);\n}\n\n.style7-calendar-header {\n    -fx-background-color: derive(-style7-color, 70%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the entry popover control.\n */\n\n.entry-popover .titled-pane .content {\n    -fx-background-color: -color-bg-default !important;\n}\n\n.popover-header {\n    -fx-padding: 10px 0px 10px 10px;\n    -fx-font-weight: bold;\n    -fx-border-color: transparent transparent -color-border-default transparent;\n    -fx-border-insets: 0px 10px 0px 10px;\n}\n\n.popover-header > .title {\n    -fx-padding: 0px;\n    -fx-font-size: 16px;\n    -fx-background-color: null;\n    -fx-border-color: null;\n}\n\n.popover-header > .location {\n    -fx-padding: 0px;\n    -fx-text-fill: -color-fg-muted;\n    -fx-background-color: null;\n    -fx-border-color: null;\n}\n\n.popover-footer {\n    -fx-padding: 10px 10px 10px 10px;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button .arrow {\n    -fx-opacity: 0px;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button {\n    -fx-min-width: 0px;\n    -fx-min-height: 0px;\n    -fx-pref-width: 0px;\n    -fx-pref-height: 0px;\n    -fx-padding: 0px;\n}\n\n.popover-accordion > .titled-pane {\n    -fx-background-color: transparent;\n    -fx-animated: true;\n    -fx-text-fill: -color-fg-muted;\n}\n\n.popover-accordion > .titled-pane:expanded {\n    -fx-text-fill: -color-fg-muted;\n}\n\n.popover-accordion > .titled-pane > .title {\n    -fx-background-color: transparent;\n    -fx-border-style: dashed;\n    -fx-border-color: -color-border-default transparent transparent transparent;\n    -fx-border-insets: 1px 10px 1px 10px;\n}\n\n.popover-accordion > .first-titled-pane > .title {\n    -fx-border-color: transparent transparent transparent transparent;\n}\n\n.popover-accordion > .titled-pane:collapsed > .title {\n    -fx-border-color: -color-border-default transparent transparent transparent;\n}\n\n.popover-accordion > .first-titled-pane:collapsed > .title {\n    -fx-border-color: transparent transparent transparent transparent;\n}\n\n.popover-accordion > .titled-pane > .content {\n    -fx-background-color: -color-bg-default;\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 6.0 0px 0px 0px;\n    -fx-padding: 20px;\n}\n\n.popover-accordion > .titled-pane.no-padding > .content {\n    -fx-padding: 0px;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button > .arrow {\n    -fx-min-width: 0px;\n    -fx-min-height: 0px;\n    -fx-pref-width: 0px;\n    -fx-pref-height: 0px;\n    -fx-padding: 0px;\n}\n\n.popover-accordion > .titled-pane:expanded > * {\n    -fx-background-color: -color-bg-default;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by Popover control from the ControlsFX library.\n */\n\n.popover  {\n    -fx-background-color: transparent;\n}\n\n.popover > .border {\n    -fx-stroke: -color-border-default;\n    -fx-stroke-width: 1;\n    -fx-fill: -color-bg-default;\n    -fx-effect: dropshadow(three-pass-box, -color-shadow-default, 16px, 0.5, 0, 2);\n}\n\n.popover > .content {\n}\n\n.popover > .detached {\n}\n\n.popover > .content > .title > .text  {\n    -fx-padding: 6.0 6.0 0.0 6.0;\n    -fx-text-fill: -color-fg-default;\n    -fx-font-weight: bold;\n}\n\n.popover > .content > .title > .icon {\n    -fx-padding: 6px 0px 0px 10px;\n}\n\n.popover > .content > .title > .icon > .graphics > .circle {\n    -fx-fill: -color-border-default;\n    -fx-effect: innershadow(gaussian, -color-shadow-default, 3, 0.5, 1.0, 1.0);\n}\n\n.popover > .content > .title > .icon > .graphics > .line {\n    -fx-stroke: -color-fg-default;\n    -fx-stroke-width: 2;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the RecurrenceView control.\n */\n\n.recurrence-view {\n}\n\n.recurrence-view > .container {\n    -fx-hgap: 1.0em;\n    -fx-vgap: 1.0em;\n}\n\n.recurrence-view > .container > .ends-after-box,\n.recurrence-view > .container > .ends-on-box,\n.recurrence-view > .container > .repeat-count-box {\n    -fx-spacing: 0.5em;\n    -fx-alignment: center-left;\n}\n\n.recurrence-view > .container > .label {\n    -fx-font-weight: bold;\n}\n\n.recurrence-view > .container > .weekday-box {\n\n}\n\n.recurrence-view > .container > .repeat-by-box {\n    -fx-spacing: 1.0em;\n}\n\n.recurrence-view > .container > .weekday-box > .toggle-button {\n    -fx-background-radius: 0px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles used for the RecurrencePopup control.\n */\n\n.recurrence-popup {\n    -fx-padding: 1.0em;\n    -fx-background-radius: 4px;\n    -fx-background-color: -color-bg-default !important;\n    -fx-effect: dropshadow(gaussian, -color-shadow-default, 10, 0.5, 2.0, 2.0);\n    -fx-border-radius: 4px;\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0.5;\n}\n\n.recurrence-popup .content {\n    -fx-background-color: -color-bg-default !important;\n}\n\n.recurrence-popup > .content > .button-pane {\n    -fx-spacing: 1.0em;\n    -fx-padding: 2.0em 1.0em 0.5em 1.0em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles used for MonthSheetView control.\n */\n\n.month-sheet-view {\n    -fx-background-color: -color-bg-default;\n}\n\n.month-sheet-view .month-header {\n    -fx-alignment: center;\n    -fx-text-alignment: center;\n    -fx-font-size: 1.1em;\n    -fx-border-color: -today-color;\n    -fx-border-width: 0px 0px 2.0 0px;\n}\n\n.month-sheet-view .date-cell {\n    -fx-padding: 2.0;\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 1px 1px 0px;\n    -fx-font-size: .8em;\n}\n\n.month-sheet-view .date-cell:today {\n    -fx-background-color: -today-color;\n}\n\n.month-sheet-view .date-cell .week-number-label {\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: 0.7em;\n    -fx-alignment: top-right;\n}\n\n.month-sheet-view .date-cell.weekend-day {\n    -fx-background-color: -weekend-fill-color;\n}\n\n.month-sheet-view > .container > .date-cell .day-of-month-label {\n    -fx-pref-width: 2.0em;\n    -fx-alignment: center;\n    -fx-font-weight: bold;\n}\n\n.month-sheet-view > .container > .date-cell.first-month {\n    -fx-border-width: 0px 1px 1px 1px;\n}\n\n.month-sheet-view > .container > .date-cell.last-month {\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.month-sheet-view > .container > .date-cell.first-day-of-week {\n}\n\n.month-sheet-view > .container > .date-cell:today > .day-of-month-label,\n.month-sheet-view > .container > .date-cell:today > .day-of-week-label,\n.month-sheet-view > .container > .date-cell:today > .week-number-label {\n    -fx-text-fill: -color-fg-default;\n    -fx-font-weight: bold;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-low {\n    -fx-background-color: -usage-low-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-medium {\n    -fx-background-color: -usage-medium-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-high {\n    -fx-background-color: -usage-high-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-low > .label {\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-low > .label {\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-medium > .label {\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-high > .label {\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-high > .label {\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell {\n    -fx-min-width: 100.0;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label {\n    -fx-pref-width: 2em;\n    -fx-background-radius: 2;\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-text-alignment: right;\n    -fx-padding: 1px 4px 1px 4px;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-low {\n    -fx-background-color: -usage-low-color;\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-medium {\n    -fx-background-color: -usage-medium-color;\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-high {\n    -fx-background-color: -usage-high-color;\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.month-sheet-view > .container > .extended-date-cell.weekend-day {\n    -fx-background-color: derive(blanchedalmond, -5%);\n}\n\n.month-sheet-view > .container > .extended-date-cell {\n}\n\n.month-sheet-view > .container > .date-cell:selected {\n    -fx-background-color: -color-accent-emphasis;\n    -fx-border-color: derive(-color-accent-emphasis, -10%);\n}\n\n.month-sheet-view > .container > .date-cell:selected > .day-of-month-label,\n.month-sheet-view > .container > .date-cell:selected > .day-of-week-label,\n.month-sheet-view > .container > .date-cell:selected > .week-number-label {\n    -fx-text-fill: -color-fg-default;\n    -fx-font-weight: bold;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PaperView control.\n */\n\n.paper-view {\n}\n\n.paper-view > .container {\n    -fx-hgap: 10;\n    -fx-vgap: 10;\n}\n\n.paper-view > .container > .custom-fields {\n    -fx-hgap: 5px;\n    -fx-vgap: 5px;\n}\n\n.paper-view > .container > .custom-fields > .text-field {\n    -fx-pref-width: 3em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the TimeRangeView control.\n */\n\n.time-range-view {\n}\n\n.time-range-view > .container {\n    -fx-hgap: 10;\n    -fx-vgap: 10;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the SettingsView control.\n */\n\n.print-settings-view {\n}\n\n.print-settings-view > .container {\n    -fx-spacing: 15px;\n}\n\n.print-settings-view > .container > .section-title > .label {\n    -fx-font-weight: bold;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the SourceGridView control.\n */\n\n.source-grid-view {\n    -fx-spacing: 7px;\n}\n\n.source-grid-view .column {\n    -fx-spacing: 5px;\n}\n\n.source-grid-view .column .item-box {\n    -fx-pref-width: 20px;\n    -fx-pref-height: 10px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PrintablePage control.\n */\n\n.print-page {\n    -fx-background-color: -color-bg-default;\n}\n\n.print-page > .container {\n    -fx-padding: 10px;\n}\n\n.print-page > .container > .header {\n    -fx-padding: 0px 0px 10px 0px;\n}\n\n.print-page > .container > .header > .title-section {\n    -fx-spacing: 20px;\n}\n\n.print-page > .container > .header > .title-section > .period-label {\n    -fx-font-size: 28.0px;\n    -fx-font-weight: bold;\n}\n\n.print-page > .container > .header > .mini-calendars {\n    -fx-padding: 0px 0px 0px 0px;\n}\n\n.print-page .year-month-view > .container {\n    -fx-padding: 5px 10px 5px 10px;\n}\n\n/*\n * The YearMonthView instances shown inside the PrintablePage control should be smaller than\n * normally. Otherwise they waste too much space.\n */\n.print-page .year-month-view > .container > .day-of-week-label,\n.print-page .year-month-view > .container > .day-of-month-label,\n.print-page .year-month-view > .container > .day-not-of-month-label {\n    -fx-font-size: 10px;\n}\n\n.print-page .year-month-view > .container > .header > .month-header > .month-label,\n.print-page .year-month-view > .container > .header > .year-header > .year-label {\n    -fx-font-size: 14px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PreviewPane control.\n */\n\n.print-preview {\n    -fx-padding: 10px;\n}\n\n.print-preview > .container > .center {\n    -fx-background-color: -color-bg-subtle;\n    -fx-border-width: 1px;\n    -fx-border-color: -color-border-default;\n}\n\n.print-preview > .container > .footer {\n    -fx-padding: 20px 0px 0px 0px;\n    -fx-spacing: 10px;\n}\n\n.print-preview > .container > .center > .zoom-pane > .print-page {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 1px;\n    -fx-effect: dropshadow(three-pass-box, -color-shadow-default, 10, 0, 0, 0);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PrintView control.\n */\n\n.print-view {\n    -fx-padding: 15px;\n    -fx-background-color: -color-bg-default;\n}\n\n.print-view > .container > .button-bar {\n    -fx-alignment: center-right;\n    -fx-spacing: 10;\n}\n\n.print-view > .container > .separator {\n    -fx-padding: 0px 10px 0px 10px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ButtonBar control.\n */\n\n.button-bar > .container > .button {\n}\n\n.button-bar > .container > .button:focused {\n}\n\n.button-bar > .container > .button.left-pill {\n    -fx-background-radius: 3px 0px 0px 3px;\n}\n\n.button-bar > .container > .button.left-pill:focused {\n    -fx-background-radius: 3px 0px 0px 3px;\n}\n\n.button-bar > .container > .button.right-pill {\n    -fx-background-radius: 0px 3px 3px 0px;\n}\n\n.button-bar > .container > .button.right-pill:focused {\n    -fx-background-radius: 0px 3px 3px 0px;\n    -fx-background-insets: -2px -2px -2px 0px, 0px, 1px 1px 1px 0px, 2px 2px 2px 1px;\n}\n\n.button-bar > .container > .button.center-pill {\n    -fx-background-radius: 0px;\n}\n\n.button-bar > .container > .button.center-pill:focused {\n    -fx-background-radius: 0px;\n    -fx-background-insets: -2px 0px -2px -1px, 0px 0px 0px -1px, 1px 1px 1px 0px, 2px 2px 2px 1px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the DetailedDayView control.\n */\n\n.detailed-day-view {\n    -fx-padding: 0px;\n}\n\n.detailed-day-view > .container > .separator {\n    -fx-padding: 5px;\n}\n\n.detailed-day-view > .container > .all-day-label {\n    -fx-font-weight: normal;\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: .9em;\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 0px 5px 0px;\n    -fx-padding: 0px 2px 0px 0px;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the DetailedWeekView control.\n */\n\n.detailed-week-view > .container > .filler-right,\n.detailed-week-view > .container > .filler-left {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.detailed-week-view > .container > .all-day-filler {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 0px 5px 0px;\n}\n\n.detailed-week-view > .container > .all-day-label {\n    -fx-font-weight: normal;\n    -fx-text-fill: -color-fg-muted;\n    -fx-font-size: .9em;\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 0px 5px 0px;\n    -fx-padding: 0px 2px 0px 0px;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the WeekFieldsView control.\n */\n\n.week-fields {\n}\n\n.week-fields > .content {\n    -fx-hgap: 10px;\n    -fx-vgap: 10px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ResourceCalendarView control.\n */\n.resource-calendar-view {\n}\n\n.resource-calendar-view .time-scale {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 1px 0px 0px;\n}\n\n.resource-calendar-view .header-background {\n    -fx-background-color: -color-border-default, -color-bg-default;\n    -fx-background-insets: 0, 0 0 1 0;\n}\n\n.resource-calendar-view .marker-line {\n    -fx-pref-height: 5px;\n    -fx-background-color: -color-warning-emphasis;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the TimeField control.\n */\n.time-field > .box {\n    -fx-spacing: 2px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ResourcesView.\n */\n.resources-view {\n    -fx-border-width: 1px;\n    -fx-border-color: -color-border-default;\n}\n\n.resources-view .week-number-label {\n    -fx-font-size: 1.2em;\n    -fx-font-weight: bold;\n}\n\n.resources-view .header-box .day-box .weekday-header-view {\n    -fx-border-color: -color-border-default;\n}\n\n.resources-view .header-box .day-box .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header {\n    -fx-padding: 5px;\n    -fx-border-color: -color-border-default;\n    -fx-font-weight: bold;\n    -fx-alignment: center;\n}\n\n.resources-view .header-box .day-box .resource-header.first {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header.only,\n.resources-view .header-box .day-box .resource-header.middle {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header.last {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n\n.resources-view .header-box .resource-header-view .resource-header {\n    -fx-padding: 5px;\n    -fx-font-weight: bold;\n    -fx-alignment: center;\n}\n\n.resources-view .header-box .resource-header-view .resource-header,\n.resources-view .header-box .resource-header-view .all-day-view,\n.resources-view .header-box .resource-header-view .weekday-header-view {\n    -fx-border-color: -color-border-default;\n}\n\n.resources-view .header-box .resource-header-view.first .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.first .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.first .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .resource-header,\n.resources-view .header-box .resource-header-view.middle .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .weekday-header-view,\n.resources-view .header-box .resource-header-view.middle .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .all-day-view,\n.resources-view .header-box .resource-header-view.middle .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view {\n    -fx-border-color: -color-border-default;\n}\n\n.resources-view .resources-view-container .week-view.first {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view.only {\n    -fx-border-width: 1px;\n}\n\n.resources-view .resources-view-container .week-view.middle {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view.last {\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .upper-left-corner {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .upper-right-corner {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 0px 1px 1px;\n}\n\n.resources-view .time-scale {\n    -fx-border-color: -color-border-default;\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .weekday-separator {\n    -size: 1px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: -color-border-default;\n}\n\n.resources-view .large-separator {\n    -size: 4px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: -color-border-default;\n}\n\n.resources-view .small-separator {\n    -size: 1px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: -color-border-default;\n}"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/calendar.css",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Base colors for the predefined styles\n */\n\n* {\n    -fx-font-family: \"Arial\";\n\n    -style1-color: rgba(119.0, 192.0, 75.0, 0.9);\n    -style2-color: rgba(65.0, 143.0, 203.0, 0.9);\n    -style3-color: rgba(247.0, 209.0, 91.0, 0.9);\n    -style4-color: rgba(157.0, 91.0, 159.0, 0.9);\n    -style5-color: rgba(208.0, 82.0, 95.0, 0.9);\n    -style6-color: rgba(249.0, 132.0, 75.0, 0.9);\n    -style7-color: rgba(174.0, 102.0, 62.0, 0.9);\n\n    -weekend-fill-color: rgb(245.0, 245.0, 245.0);\n    -today-fill-color: rgb(232, 77, 62);\n    -today-stroke-color: rgb(232, 77, 62);\n\n    -usage-very-low-color: lightgreen;\n    -usage-low-color: green;\n    -usage-medium-color: gold;\n    -usage-high-color: darkorange;\n    -usage-very-high-color: red;\n\n    -usage-very-low-text-color: green;\n    -usage-low-text-color: lightgreen;\n    -usage-medium-text-color: black;\n    -usage-high-text-color: black;\n    -usage-very-high-text-color: white;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * A few ID-based styles.\n */\n\n#toolbar {\n    -fx-padding: 10.0;\n    -fx-border-width: 0.0 0.0 0.5 0.0;\n    -fx-border-color: gray;\n}\n\n#switcher {\n\n}\n\n#search-field {\n    -fx-background-radius: 100.0px;\n    -fx-pref-width: 200.0px;\n}\n\n#search-icon {\n    -glyph-name: \"SEARCH\";\n    -glyph-size: 1.0em;\n}\n\n#date-picker {\n    -fx-border-color: lightgray transparent transparent transparent;\n    -fx-border-insets: 0.0 6.0 0.0 6.0;\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n    -fx-min-width: 200.0px;\n    -fx-min-height: 180.0px;\n}\n\n#tray-pane {\n    -fx-border-color: null;\n}\n\n.date-hyperlink {\n    -fx-cursor: hand;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for CalendarView.\n */\n\n.calendar-view .master-detail-pane > .split-pane > .split-pane-divider {\n    -fx-padding: 0.0 1.0 0.0 0.0;\n}\n\n.calendar-view .master-detail-pane > .split-pane {\n    -fx-background-color: transparent;\n    -fx-background-insets: 0.0, 0.0;\n    -fx-padding: 0.0;\n}\n\n.calendar-view .button-icon {\n    -fx-icon-color: rgb(100, 100, 100);\n    -fx-icon-size: 14.0px;\n}\n\n.right-toolbar-container {\n    -fx-spacing: 10px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PageBase abstract superclass of the day, week, month, and year page.\n * The page base consists of a big content node in the middle and a header that displays\n * navigation buttons to go forward, back, to today and the currently shown date (range).\n */\n\n.calendar-page {\n    -fx-background-color: white;\n}\n\n.calendar-page > .container > .content {\n    -fx-padding: 10px;\n}\n\n.calendar-page > .container > .header {\n}\n\n.calendar-page > .container > .header > .date-text {\n    -fx-font-size: 2.2em;\n}\n\n.calendar-page > .container > .header > .navigate-date-view {\n}\n\n.calendar-page > .container > .header > .navigate-date-view > .container > .previous-date-button {\n    -fx-padding: 3 7 3 7;\n}\n\n.calendar-page > .container > .header > .navigate-date-view > .container > .next-date-button {\n    -fx-padding: 3 7 3 7;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the \"days\" page.\n */\n\n.day-page > .container > .content > .left-side > .header {\n    -fx-background-color: rgba(0, 0, 0, .1);\n    -fx-background-radius: 4px;\n    -fx-padding: 0 1em 0 1em;\n}\n\n.day-page > .container > .content > .left-side > .header > .today-label {\n    -fx-font-size: 8.0em;\n    -fx-text-fill: -fx-text-background-color;\n    -fx-opacity: 0.9;\n}\n\n.day-page > .container > .content > .left-side > .agenda-view {\n    -fx-padding: 2em 0 0 0;\n    -fx-background-insets: 0.0px;\n}\n\n.day-page .day-view {\n    -fx-pref-width: 350px;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-week-label {\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-padding: 0.0 0.0 5.0 0.0;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .week-label,\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-week-label {\n    -fx-text-fill: -fx-text-background-color;\n    -fx-font-size: 10px;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .week-label {\n    -fx-padding: 0.0 8.0 0.0 0.0;\n    -fx-min-width: 2.0em;\n    -fx-min-height: 2.0em;\n    -fx-text-fill: rgba(0, 0, 0, .5);\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .current-date-label {\n    -fx-text-fill: -today-stroke-color;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .current-date-border {\n    -fx-border-color: transparent transparent -today-stroke-color transparent;\n    -fx-border-width: 2.0;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .month-day {\n    -fx-padding: .2em .3em .2em .3em;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-of-month-label {\n    -fx-text-fill: -fx-text-background-color;\n    -fx-font-size: .9em;\n    -fx-opacity: 0.8;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .day-not-of-month-label {\n    -fx-text-fill: -fx-text-background-color;\n    -fx-opacity: .5;\n    -fx-text-stroke: black;\n}\n\n.day-page > .container > .content > .left-side > .header > .year-month-view > .container > .weekend-day {\n    -fx-background-color: transparent;\n}\n\n.toolbar-controls-box {\n    -fx-spacing: 5px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the DatePopOver control.\n */\n\n.date-popover > .border {\n    -fx-fill: linear-gradient(#f6f6f6 50.0%, #e6e6e6 100.0%) !important;\n}\n\n.date-popover > .content > .entries-pane {\n    -fx-padding: 0.5em;\n}\n\n.date-popover > .content > .entries-pane > .entry > .title {\n    -fx-padding: 2.0;\n    -fx-font-size: 12px;\n}\n\n.date-popover > .content > .no-entries-label {\n    -fx-padding: 4.0;\n    -fx-font-size: 14px;\n}\n\n.date-popover > .content > .entries-pane > .entry > .time {\n    -fx-padding: 2.0 2.0 2.0 40.0;\n    -fx-font-size: 10px;\n    -fx-text-fill: gray;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for the EntryDetailsView.\n */\n\n.entry-details-view > .content {\n    -fx-hgap: 1.0em;\n    -fx-vgap: 1.0em;\n}\n\n.entry-details-view > .content > .recurrence-summary-label {\n    -fx-font-weight: bold;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the source view. This control displays the currently known\n * calendar sources and for each source the calendars inside of it.\n */\n\n.source-view > .container > .titled-pane > .title > .arrow-button .arrow {\n    -fx-opacity: 0.0;\n}\n\n.source-view > .container > .titled-pane > .title > .arrow-button {\n    -fx-pref-width: 0.0;\n    -fx-pref-height: 0.0;\n}\n\n.source-view > .container > .titled-pane {\n    -fx-background-color: transparent;\n    -fx-animated: true;\n}\n\n.source-view > .container > .titled-pane:expanded {\n}\n\n.source-view > .container > .titled-pane > .title {\n    -fx-font-weight: bold;\n    -fx-background-color: transparent;\n    -fx-border-color: transparent transparent transparent transparent;\n    -fx-border-insets: 1.0 10.0 1.0 10.0;\n}\n\n.source-view > .container > .titled-pane:collapsed > .title {\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-effect: null;\n}\n\n.source-view > .container > .titled-pane > .content {\n    -fx-border-color: transparent;\n    -fx-padding: 0.0 20.0 0.0 20.0;\n    -fx-background-color: transparent;\n}\n\n.source-view > .container > .titled-pane > .title > .arrow-button > .arrow {\n    -fx-pref-width: 0.0;\n    -fx-pref-height: 0.0;\n}\n\n.source-view > .container > .single-calendar-group {\n    -fx-padding: 1em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the scroll pane that wraps the source view.\n */\n\n.source-view-scroll-pane,\n.source-view-scroll-pane:focused {\n    -fx-padding: 0.0;\n    -fx-background-radius: 0.0;\n    -fx-background-insets: 0.0;\n    -fx-background-color: transparent;\n    -fx-border-radius: 0.0;\n    -fx-border-insets: 0.0;\n    -fx-border-color: transparent;\n    -fx-fit-to-width: true;\n    -fx-fit-to-height: true;\n}\n\n.source-view-scroll-pane .corner {\n    -fx-background-insets: 0.0px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the year view.\n */\n\n.year-view .scroll-pane {\n    -fx-padding: 0px;\n    -fx-background-insets: 0px;\n}\n\n.year-view .scroll-pane .container {\n    -fx-background-color: white;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the year month view.\n */\n.year-month-view > .container > .header {\n    -fx-padding: 0.0 0.0 10.0 0.0;\n}\n\n.year-month-view > .container > .header > .month-header {\n}\n\n.year-month-view > .container > .header > .year-header {\n}\n\n.year-month-view > .container > .header > .month-header > .month-label {\n    -fx-font-size: 18px;\n}\n\n.year-month-view > .container > .header > .year-header > .year-label {\n    -fx-font-size: 18px;\n}\n\n.year-month-view > .container {\n    -fx-padding: 1em;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button,\n.year-month-view > .container > .header > .year-header > .next-button,\n.year-month-view > .container > .header > .month-header > .previous-button,\n.year-month-view > .container > .header > .month-header > .next-button {\n    -fx-background-color: gray;\n    -fx-pref-width: 8.0;\n    -fx-pref-height: 8.0;\n    -fx-max-width: 8.0;\n    -fx-max-height: 8.0;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button:hover,\n.year-month-view > .container > .header > .year-header > .next-button:hover,\n.year-month-view > .container > .header > .month-header > .previous-button:hover,\n.year-month-view > .container > .header > .month-header > .next-button:hover {\n    -fx-background-color: lightgray;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button:pressed,\n.year-month-view > .container > .header > .year-header > .next-button:pressed,\n.year-month-view > .container > .header > .month-header > .previous-button:pressed,\n.year-month-view > .container > .header > .month-header > .next-button:pressed {\n    -fx-background-color: gray;\n}\n\n.year-month-view > .container > .header > .year-header > .previous-button,\n.year-month-view > .container > .header > .month-header > .previous-button {\n    -fx-shape: \"M3,0 V3 L0 1.5 L3,0 Z\";\n}\n\n.year-month-view > .container > .header > .year-header > .next-button,\n.year-month-view > .container > .header > .month-header > .next-button {\n    -fx-shape: \"M0,0 L3 1.5 L0,3 V0 Z\";\n}\n\n.year-month-view > .container > .day-of-week-label {\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-padding: 0.0 0.0 5.0 0.0;\n}\n\n.year-month-view > .container > .week-label,\n.year-month-view > .container > .day-of-week-label {\n    -fx-text-fill: gray;\n    -fx-font-size: 10px;\n}\n\n.year-month-view > .container > .week-label {\n    -fx-padding: 0.0 2.0 0.0 0.0;\n    -fx-min-width: 2.0em;\n    -fx-min-height: 2.0em;\n}\n\n.year-month-view > .container > .current-date-label {\n    -fx-text-fill: -today-stroke-color;\n}\n\n.year-month-view > .container > .current-date-border {\n    -fx-border-color: transparent transparent -today-stroke-color transparent;\n    -fx-border-width: 2.0;\n}\n\n.year-month-view > .container > .month-day {\n    -size: 2em;\n    -fx-pref-width: -size;\n    -fx-pref-height: -size;\n    -fx-min-width: -size;\n    -fx-min-height: -size;\n}\n\n.year-month-view > .container > .day-of-month-label {\n    -fx-text-fill: rgba(0.0, 0.0, 0.0, 0.66);\n}\n\n.year-month-view > .container > .day-not-of-month-label {\n    -fx-text-fill: lightgray;\n}\n\n.year-month-view > .container > .weekend-day {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n.year-month-view > .container > .usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.year-month-view > .container > .usage-low {\n    -fx-background-color: -usage-low-color;\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.year-month-view > .container > .usage-medium {\n    -fx-background-color: -usage-medium-color;\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.year-month-view > .container > .usage-high {\n    -fx-background-color: -usage-high-color;\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.year-month-view > .container > .usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.year-month-view > .container > .selected-month-date {\n    -fx-background-color: lightgray;\n    -fx-text-fill: black;\n    -fx-font-weight: bold;\n    -fx-background-radius: 10em;\n}\n\n.year-month-view > .container > .today {\n    -fx-background-color: -today-fill-color;\n    -fx-text-fill: white !important;\n    -fx-font-weight: bold;\n    -fx-background-radius: 10em;\n    -fx-opacity: 1 !important;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the month view.\n */\n\n.month-view > .container > .day {\n    -fx-padding: 0.0px;\n    -fx-background-insets: 0.0px;\n    -fx-border-color: lightgray transparent transparent transparent;\n    -fx-border-insets: 0.0;\n    -fx-border-width: 1.0 0.0 0.0 0.0;\n}\n\n.month-view > .container > .day:selected {\n    -fx-background-color: aliceblue;\n}\n\n.month-view > .container > .today,\n.month-view > .container > .current-week {\n    -fx-border-insets: 0.0;\n    -fx-border-width: 5.0 0.0 0.0 0.0;\n}\n\n.month-view > .container > .current-week {\n    -fx-border-color: salmon transparent transparent transparent;\n}\n\n.month-view > .container > .today {\n    -fx-background-color: lavenderblush;\n    -fx-text-fill: red;\n    -fx-border-color: -today-stroke-color transparent transparent transparent;\n}\n\n.month-view > .container > .day > .entries-pane {\n    -fx-spacing: 1.0px;\n}\n\n.month-view > .container > .day > .header {\n    -fx-padding: 2.0 4.0 2.0 0.0;\n}\n\n.month-view > .container > .day > .header > .day-of-month-label,\n.month-view > .container > .day > .header > .day-not-of-month-label {\n    -fx-alignment: center-right;\n    -fx-font-weight: bold;\n}\n\n.month-view > .container > .day > .header > .day-of-month-label {\n    -fx-text-fill: derive(gray, -40.0%);\n}\n\n.month-view > .container > .day > .header > .day-not-of-month-label {\n    -fx-text-fill: lightgray;\n}\n\n.month-view > .container > .day > .header > .weekend-day {\n    -fx-text-fill: gray;\n}\n\n.month-view > .container > .day > .header > .week-of-year-label,\n.month-view > .container > .day > .header > .current-week-of-year-label {\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-padding: 0.25em;\n}\n\n.month-view > .container > .day > .header > .week-of-year-label {\n    -fx-text-fill: white;\n    -fx-background-color: gray;\n}\n\n.month-view > .container > .day > .header > .current-week-of-year-label {\n    -fx-text-fill: lightcoral;\n    -fx-background-color: lavenderblush;\n}\n\n.month-view > .container > .day-of-week-label,\n.month-view > .container > .day-of-weekend-label {\n    -fx-text-fill: derive(gray, -40.0%);\n    -fx-padding: 0.0 0.0 5.0px 0.0;\n    -fx-font-size: 1.1em;\n    -fx-font-weight: bold;\n}\n\n.month-view > .container > .day-of-weekend-label {\n    -fx-text-fill: derive(gray, 10.0%);\n}\n\n.month-view > .container > .weekend-day {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n.month-view > .container > .day > .entries-pane > .more-label {\n    -fx-text-fill: gray;\n    -fx-font-weight: bold;\n    -fx-padding: 0.0 0.0 0.0 10.0px;\n}\n\n.month-view > .container > .day > .header > .today-label {\n    -fx-text-fill: -today-stroke-color;\n    -fx-background-insets: 0.0 4.0px 0.0 4.0px;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the week day separators.\n */\n.week-view .weekday-separator,\n.weekday-header-view .weekday-separator,\n.all-day-view .weekday-separator {\n    -size: 0px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: lightgrey;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the day view.\n */\n\n.day-view {\n    -fx-padding: 0.0 0.0 0.0 0.0;\n}\n\n.day-view.today {\n}\n\n.day-view.weekend-day {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n.day-view > .half-hour-line {\n    -fx-stroke: derive(lightgray, +40%);\n}\n\n.day-view > .full-hour-line {\n    -fx-stroke: lightgray;\n}\n\n.day-view > .midnight-line {\n    -fx-stroke-width: 2;\n    -fx-stroke: darkgrey;\n}\n\n.day-view > .noon-line {\n    -fx-stroke-width: 2;\n    -fx-stroke: darkgrey;\n    -fx-stroke-dash-array: 6 6;\n}\n\n.day-view > .current-time-line {\n    -fx-stroke: -today-stroke-color;\n}\n\n.day-view > .early-hour-line,\n.day-view > .late-hour-line {\n    -fx-opacity: 0.5;\n}\n\n.day-view > .early-hours-region,\n.day-view > .late-hours-region {\n    -fx-background-color: rgba(200.0, 200.0, 200.0, 0.2);\n}\n\n.day-view > .current-time-circle {\n    -fx-stroke: -today-fill-color;\n    -fx-fill: -today-stroke-color;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the weekday view.\n */\n\n.weekday-view {\n}\n\n.weekday-view.today {\n}\n\n.weekday-view.weekend-day {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the time scale (01:00, 02:00, .... 24:00).\n */\n\n.time-scale {\n    -fx-padding: 0.0 5.0 0.0 5.0;\n}\n\n.time-scale > .time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: gray;\n}\n\n.time-scale > .date-label {\n    -fx-font-size: 0.8em;\n    -fx-background-color: gray;\n    -fx-background-radius: 2px;\n    -fx-padding: 2px 4px;\n    -fx-text-fill: white;\n}\n\n.time-scale > .current-time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: -today-stroke-color;\n}\n\n.time-scale > .early-hour-label,\n.time-scale > .late-hour-label {\n    -fx-opacity: 0.5;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the WeekDayHeaderView.\n */\n\n.weekday-header-view > .container > .cell {\n    -fx-font-size: 1.1em;\n    -fx-font-weight: bold;\n    -fx-padding: 0.0 0.0 4.0 0.0;\n    -fx-border-insets: 0.0 0.0 0.0 0.0;\n    -fx-alignment: center;\n    -fx-border-color: transparent transparent lightgray transparent;\n}\n\n.weekday-header-view > .container > .today {\n    -fx-text-fill: -today-fill-color;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the AllDayView.\n */\n\n.all-day-view {\n    -fx-min-height: 2.0em;\n    -fx-row-height: 20.0px; /* custom styleable property */\n    -fx-row-spacing: 2.0px; /* custom styleable property */\n    -fx-column-spacing: 2.0px; /* custom styleable property */\n}\n\n.all-day-view > .container > .day-region {\n    -fx-border-width: 0.0 0.0 5.0 0.0;\n    -fx-border-color: transparent transparent lightgray transparent;\n}\n\n.all-day-view > .container > .today {\n    -fx-border-color: transparent transparent -today-fill-color transparent;\n    -fx-background-color: lavenderblush;\n}\n\n.all-day-view > .container > .weekend {\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the AgendaView.\n */\n\n.agenda-view > .container > .list-view > .placeholder-label {\n    -fx-text-fill: gray;\n    -fx-font-size: 2.5em;\n}\n\n.agenda-view > .container {\n    -fx-padding: 0.0;\n}\n\n.agenda-view > .container > .list-view {\n    -fx-background-color: transparent;\n}\n\n.agenda-view > .container > .status-label {\n    -fx-text-fill: gray;\n    -fx-alignment: center;\n}\n\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical,\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical .decrement-arrow,\n.agenda-view > .container > .list-view > .virtual-flow > .scroll-bar:vertical .increment-arrow {\n    -fx-pref-width: 0.0;\n}\n\n.agenda-view-list-cell {\n    -fx-background-color: transparent;\n}\n\n.agenda-view-list-cell > .container > .header {\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-border-width: 3.0;\n    -fx-border-insets: 0.0 0.0 4.0 0.0;\n}\n\n.agenda-view-list-cell > .container > .header.today {\n    -fx-border-color: transparent transparent -today-fill-color transparent;\n}\n\n.agenda-view-list-cell > .container > .header > .date-label,\n.agenda-view-list-cell > .container > .header > .weekday-label {\n    -fx-padding: 0.0 0.0 4.0 0.0;\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-text-fill: gray;\n}\n\n.agenda-view-list-cell > .container > .header > .today,\n.agenda-view-list-cell > .container > .header > .today {\n    -fx-text-fill: -today-fill-color;\n}\n\n.agenda-view-list-cell > .container > .body {\n    -fx-padding: 0.0 0.0 20.0 0.0;\n    -fx-vgap: 5.0px;\n    -fx-hgap: 8.0px;\n}\n\n.agenda-view-list-cell > .container > .body > .title-label {\n    -fx-padding: 4.0 4.0 4.0 0.0;\n    -fx-text-fill: black;\n    -fx-font-weight: bold;\n}\n\n.agenda-view-list-cell > .container > .body > .time-label {\n    -fx-text-fill: gray;\n}\n\n.agenda-view-list-cell > .container > .body > .separator {\n    -fx-pref-height: 1.0;\n    -fx-border-color: lightgray;\n    -fx-border-style: dashed none none none;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the SearchResultView.\n */\n\n.search-result-view > .list-view,\n.search-result-view > .list-view:focused {\n    -fx-pref-width: 200.0px;\n    -fx-background-color: rgb(245.0, 245.0, 245.0);\n}\n\n.search-result-view > .list-view > .placeholder-label {\n    -fx-text-fill: gray;\n    -fx-font-size: 1.5em;\n}\n\n.search-result-cell:selected {\n    -fx-background-color: rgb(235.0, 235.0, 235.0);\n}\n\n.search-result-cell:filled:focused:selected {\n    -fx-background-color: rgb(235.0, 235.0, 235.0);\n}\n\n.search-result-cell {\n    -fx-background-color: transparent;\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-border-insets: 0.0 6.0 0.0 6.0;\n}\n\n.search-result-cell > .container > .title-label {\n    -fx-font-weight: bold;\n    -fx-font-size: 0.9em;\n    -fx-text-fill: black;\n}\n\n.search-result-cell > .container > .date-time-pane > .date-label,\n.search-result-cell > .container > .date-time-pane > .time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: gray;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the MonthEntryView.\n */\n\n.month-entry-view {\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Default styles for calendar entries.\n */\n\n.default-style-entry {\n    -fx-background-color: magenta;\n    -fx-background-radius: 0px;\n    -fx-background-insets: 0.0 0.0 0.0 2.0;\n    -fx-border-insets: 0.0 0.0 0.0 2.0;\n    -fx-border-width: 0.0 0.0 0.0 4.0;\n}\n\n.default-style-entry:dragged {\n    -fx-opacity: 0.5;\n}\n\n.default-style-entry:dragged-start,\n.default-style-entry:dragged-end {\n    -fx-opacity: 0.0;\n}\n\n.default-style-entry:selected {\n    -fx-border-width: 0.0;\n}\n\n.default-style-entry .icon-flow-pane {\n    -fx-hgap: 0px;\n    -fx-vgap: 0px;\n}\n\n.default-style-entry-time-label {\n    -fx-font-size: 0.9em;\n    -fx-padding: 4.0 4.0 2.0 4.0;\n}\n\n.default-style-entry-title-label {\n    -fx-font-size: 0.9em;\n    -fx-font-weight: bold;\n    -fx-padding: 0.0 4.0 0.0 4.0;\n}\n\n.default-style-entry-title-label-full-day {\n    -fx-font-size: 0.9em;\n    -fx-font-weight: bold;\n    -fx-padding: 0.0 4.0 0.0 4.0;\n}\n\n.default-style-entry:selected .default-style-entry-time-label {\n    -fx-text-fill: white;\n}\n\n.default-style-entry:selected .default-style-entry-title-label {\n    -fx-text-fill: white;\n}\n\n.default-style-visibility-checkbox > .box {\n\n}\n\n.default-style-visibility-checkbox > .box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;\n    -fx-background-radius: 3.0, 2.0, 1.0;\n    -fx-padding: 0.166667em 0.166667em 0.25em 0.25em; /* 2 2 3 3 */\n}\n\n.default-style-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color, -fx-faint-focus-color, -fx-body-color;\n    -fx-background-insets: -0.2, 1.0, 2.0, -1.4, 2.6;\n    -fx-background-radius: 3.0, 2.0, 1.0, 4.0, 1.0;\n}\n\n.default-style-entry-popover-title {\n}\n\n/* Small entries */\n.default-style-entry-small {\n    -fx-padding: 0.15em 0.5em 0.15em 0.5em;\n    -fx-font-size: 0.9em;\n}\n\n.default-style-entry-small-only, .default-style-entry-small-last {\n    -fx-background-insets: 0.0 2.0 0.0 0.0;\n}\n\n.default-style-entry-small > .default-style-entry-small-title-label {\n}\n\n.all-day-view .default-style-entry-small > .default-style-entry-small-title-label {\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small > .default-style-entry-small-time-label {\n}\n\n.default-style-entry-small:selected > .default-style-entry-small-title-label {\n    -fx-font-weight: bold;\n    -fx-text-fill: white;\n}\n\n.default-style-entry-small:selected > .default-style-entry-small-time-label {\n    -fx-text-fill: white;\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small:selected > * > .default-style-icon,\n.default-style-entry-small:selected > * > .default-style-icon-small {\n    -fx-stroke: white;\n    -fx-fill: white;\n}\n\n.default-style-entry-small-time-label {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: gray;\n}\n\n.default-style-entry-small-time-label-full-day {\n    -fx-font-size: 0.8em;\n    -fx-text-fill: derive(gray, -25.0%);\n}\n\n.default-style-entry-small-title-label {\n    -fx-text-fill: derive(gray, -40.0%);\n}\n\n.default-style-entry-small-title-label-full-day {\n    -fx-text-fill: derive(gray, -40.0%);\n}\n\n.default-style-entry-small-full-day {\n\n}\n\n.default-style-entry-small-full-day:selected > .default-style-entry-small-time-label {\n    -fx-text-fill: white;\n    -fx-font-weight: bold;\n}\n\n.default-style-entry-small-full-day:selected > .default-style-entry-small-title-label {\n    -fx-text-fill: white;\n    -fx-font-weight: bold;\n}\n\n.default-style-calendar-header {\n    -fx-font-weight: bold;\n    -fx-text-fill: black;\n    -fx-background-color: magenta;\n    -fx-alignment: center;\n    -fx-padding: 0.25em;\n    -fx-text-overrun: clip;\n}\n\n.default-style-calendar-header:first,\n.default-style-calendar-header:middle,\n.default-style-calendar-header:last {\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 1 (green).\n */\n\n.style1-icon, .style1-icon-small {\n    -fx-fill: -style1-color;\n    -fx-stroke: -style1-color;\n}\n\n.style1-entry {\n    -fx-background-color: derive(-style1-color, 70.0%);\n    -fx-border-color: -style1-color;\n}\n\n.style1-entry:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-entry-time-label, .style1-entry-title-label {\n    -fx-text-fill: derive(-style1-color, -30.0%);\n}\n\n.style1-entry-small:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-entry-small-full-day {\n    -fx-background-color: derive(-style1-color, 70.0%);\n}\n\n.style1-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style1-color, -30.0%);\n}\n\n.style1-entry-small-full-day:selected {\n    -fx-background-color: -style1-color;\n}\n\n.style1-visibility-checkbox > .box,\n.style1-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style1-color, 70.0%);\n}\n\n.style1-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style1-color, 70.0%);\n}\n\n.style1-entry-popover-title {\n    -fx-text-fill: derive(-style1-color, -50.0%) !important;\n}\n\n.style1-calendar-header {\n    -fx-background-color: derive(-style1-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 2 (blue).\n */\n\n.style2-icon, .style2-icon-small {\n    -fx-fill: -style2-color;\n    -fx-stroke: -style2-color;\n}\n\n.style2-entry {\n    -fx-background-color: derive(-style2-color, 70.0%);\n    -fx-border-color: -style2-color;\n}\n\n.style2-entry:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-entry-time-label, .style2-entry-title-label {\n    -fx-text-fill: derive(-style2-color, -50.0%);\n}\n\n.style2-entry-small:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-entry-small-full-day {\n    -fx-background-color: derive(-style2-color, 70.0%);\n}\n\n.style2-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style2-color, -30.0%);\n}\n\n.style2-entry-small-full-day:selected {\n    -fx-background-color: -style2-color;\n}\n\n.style2-visibility-checkbox > .box,\n.style2-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style2-color, 70.0%);\n}\n\n.style2-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style2-color, 70.0%);\n}\n\n.style2-entry-popover-title {\n    -fx-text-fill: derive(-style2-color, -30.0%) !important;\n}\n\n.style2-calendar-header {\n    -fx-background-color: derive(-style2-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 3 (yellow).\n */\n\n.style3-icon, .style3-icon-small {\n    -fx-fill: -style3-color;\n    -fx-stroke: -style3-color;\n}\n\n.style3-entry {\n    -fx-background-color: derive(-style3-color, 70.0%);\n    -fx-border-color: -style3-color;\n}\n\n.style3-entry:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-entry-time-label, .style3-entry-title-label {\n    -fx-text-fill: derive(-style3-color, -50.0%);\n}\n\n.style3-entry-small:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-entry-small-full-day {\n    -fx-background-color: derive(-style3-color, 70.0%);\n}\n\n.style3-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style3-color, -30.0%);\n}\n\n.style3-entry-small-full-day:selected {\n    -fx-background-color: -style3-color;\n}\n\n.style3-visibility-checkbox > .box,\n.style3-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style3-color, 70.0%);\n}\n\n.style3-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style3-color, 70.0%);\n}\n\n.style3-entry-popover-title {\n    -fx-text-fill: derive(-style3-color, -30.0%) !important;\n}\n\n.style3-calendar-header {\n    -fx-background-color: derive(-style3-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 4 (purple).\n */\n\n.style4-icon, .style4-icon-small {\n    -fx-fill: -style4-color;\n    -fx-stroke: -style4-color;\n}\n\n.style4-entry {\n    -fx-background-color: derive(-style4-color, 70.0%);\n    -fx-border-color: -style4-color;\n}\n\n.style4-entry:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-entry-time-label, .style4-entry-title-label {\n    -fx-text-fill: derive(-style4-color, -50.0%);\n}\n\n.style4-entry-small:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-entry-small-full-day {\n    -fx-background-color: derive(-style4-color, 70.0%);\n}\n\n.style4-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style4-color, -30.0%);\n}\n\n.style4-entry-small-full-day:selected {\n    -fx-background-color: -style4-color;\n}\n\n.style4-visibility-checkbox > .box,\n.style4-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style4-color, 70.0%);\n}\n\n.style4-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style4-color, 70.0%);\n}\n\n.style4-entry-popover-title {\n    -fx-text-fill: derive(-style4-color, -30.0%) !important;\n}\n\n.style4-calendar-header {\n    -fx-background-color: derive(-style4-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 5 (red).\n */\n\n.style5-icon, .style5-icon-small {\n    -fx-fill: -style5-color;\n    -fx-stroke: -style5-color;\n}\n\n.style5-entry {\n    -fx-background-color: derive(-style5-color, 70.0%);\n    -fx-border-color: -style5-color;\n}\n\n.style5-entry:selected {\n    -fx-background-color: -style5-color;\n}\n\n.style5-entry-time-label, .style5-entry-title-label {\n    -fx-text-fill: derive(-style5-color, -50.0%);\n}\n\n.style5-entry-small:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style5-entry-small-full-day {\n    -fx-background-color: derive(-style5-color, 70.0%);\n}\n\n.style5-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style5-color, -30.0%);\n}\n\n.style5-entry-small-full-day:selected {\n    -fx-background-color: -style5-color;\n}\n\n.style5-visibility-checkbox > .box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style5-color, 70.0%);\n}\n\n.style5-visibility-checkbox:focused > .box,\n.style5-source-grid-item-box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style5-color, 70.0%);\n}\n\n.style5-entry-popover-title {\n    -fx-text-fill: derive(-style5-color, -30.0%) !important;\n}\n\n.style5-calendar-header {\n    -fx-background-color: derive(-style5-color, 70.0%);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 6 (orange).\n */\n\n.style6-icon, .style6-icon-small {\n    -fx-fill: -style6-color;\n    -fx-stroke: -style6-color;\n}\n\n.style6-entry {\n    -fx-background-color: derive(-style6-color, 70.0%);\n    -fx-border-color: -style6-color;\n}\n\n.style6-entry:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-entry-time-label, .style6-entry-title-label {\n    -fx-text-fill: derive(-style6-color, -50.0%);\n}\n\n.style6-entry-small:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-entry-small-full-day {\n    -fx-background-color: derive(-style6-color, 70.0%);\n}\n\n.style6-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style6-color, -30.0%);\n}\n\n.style6-entry-small-full-day:selected {\n    -fx-background-color: -style6-color;\n}\n\n.style6-visibility-checkbox > .box,\n.style6-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style6-color, 70.0%);\n}\n\n.style6-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style6-color, 70.0%);\n}\n\n.style6-entry-popover-title {\n    -fx-text-fill: derive(-style6-color, -30.0%) !important;\n}\n\n.style6-calendar-header {\n    -fx-background-color: derive(-style6-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles needed for calendar type 7 (brown).\n */\n\n.style7-icon, .style7-icon-small {\n    -fx-fill: -style7-color;\n    -fx-stroke: -style7-color;\n}\n\n.style7-entry {\n    -fx-background-color: derive(-style7-color, 70.0%);\n    -fx-border-color: -style7-color;\n}\n\n.style7-entry:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-entry-time-label, .style7-entry-title-label {\n    -fx-text-fill: derive(-style7-color, -50.0%);\n}\n\n.style7-entry-small:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-entry-small-full-day {\n    -fx-background-color: derive(-style7-color, 70.0%);\n}\n\n.style7-entry-small-title-label-full-day {\n    -fx-text-fill: derive(-style7-color, -30.0%);\n}\n\n.style7-entry-small-full-day:selected {\n    -fx-background-color: -style7-color;\n}\n\n.style7-visibility-checkbox > .box,\n.style7-source-grid-item-box {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,\n    -fx-inner-border, derive(-style7-color, 70.0%);\n}\n\n.style7-visibility-checkbox:focused > .box {\n    -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color,\n    -fx-faint-focus-color, derive(-style7-color, 70.0%);\n}\n\n.style7-entry-popover-title {\n    -fx-text-fill: derive(-style7-color, -30.0%) !important;\n}\n\n.style7-calendar-header {\n    -fx-background-color: derive(-style7-color, 70.0%);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the entry popover control.\n */\n\n.popover-header {\n    -fx-padding: 10.0 0.0 10.0 10.0;\n    -fx-font-weight: bold;\n    -fx-border-color: transparent transparent lightgray transparent;\n    -fx-border-insets: 0.0 10.0 0.0 10.0;\n}\n\n.popover-header > .title {\n    -fx-padding: 0.0;\n    -fx-text-fill: green;\n    -fx-font-size: 16px;\n    -fx-background-color: null;\n    -fx-border-color: null;\n}\n\n.popover-header > .location {\n    -fx-padding: 0.0;\n    -fx-text-fill: gray;\n    -fx-background-color: null;\n    -fx-border-color: null;\n}\n\n.popover-footer {\n    -fx-padding: 10.0 10.0 10.0 10.0;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button .arrow {\n    -fx-opacity: 0.0;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button {\n    -fx-pref-width: 0.0;\n    -fx-pref-height: 0.0;\n}\n\n.popover-accordion > .titled-pane {\n    -fx-background-color: transparent;\n    -fx-animated: true;\n    -fx-text-fill: gray;\n}\n\n.popover-accordion > .titled-pane:expanded {\n    -fx-text-fill: gray;\n}\n\n.popover-accordion > .titled-pane > .title {\n    -fx-background-color: transparent;\n    -fx-border-style: dashed;\n    -fx-border-color: lightgray transparent transparent transparent;\n    -fx-border-insets: 1.0 10.0 1.0 10.0;\n}\n\n.popover-accordion > .first-titled-pane > .title {\n    -fx-border-color: transparent transparent transparent transparent;\n}\n\n.popover-accordion > .titled-pane:collapsed > .title {\n    -fx-border-color: lightgray transparent transparent transparent;\n}\n\n.popover-accordion > .first-titled-pane:collapsed > .title {\n    -fx-border-color: transparent transparent transparent transparent;\n}\n\n.popover-accordion > .titled-pane > .content {\n    -fx-background-color: rgba(245.0, 245.0, 235.0, 0.5);\n    -fx-border-color: linear-gradient(to bottom, rgba(0.0, 0.0, 0.0, 0.2), rgba(0.0, 0.0, 0.0, 0.0));\n    -fx-border-width: 6.0 0.0 0.0 0.0;\n    -fx-padding: 20.0;\n}\n\n.popover-accordion > .titled-pane.no-padding > .content {\n    -fx-padding: 0.0;\n}\n\n.popover-accordion > .titled-pane > .title > .arrow-button > .arrow {\n    -fx-pref-width: 0.0;\n    -fx-pref-height: 0.0;\n}\n\n.popover-accordion > .titled-pane:expanded > * {\n    -fx-background-color: rgba(240.0, 240.0, 240.0, 0.95);\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles needed by the RecurrenceView control.\n */\n\n.recurrence-view {\n    -fx-background-color: white;\n}\n\n.recurrence-view > .container {\n    -fx-hgap: 1.0em;\n    -fx-vgap: 1.0em;\n}\n\n.recurrence-view > .container > .ends-after-box,\n.recurrence-view > .container > .ends-on-box,\n.recurrence-view > .container > .repeat-count-box {\n    -fx-spacing: 0.5em;\n    -fx-alignment: center-left;\n}\n\n.recurrence-view > .container > .label {\n    -fx-font-weight: bold;\n}\n\n.recurrence-view > .container > .weekday-box {\n\n}\n\n.recurrence-view > .container > .repeat-by-box {\n    -fx-spacing: 1.0em;\n}\n\n.recurrence-view > .container > .weekday-box > .toggle-button {\n    -fx-background-radius: 0.0;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles used for the RecurrencePopup control.\n */\n\n.recurrence-popup {\n    -fx-padding: 1.0em;\n    -fx-background-color: rgba(255.0, 255.0, 255.0, 0.95);\n    -fx-background-radius: 4.0px;\n    -fx-effect: dropshadow(gaussian, rgba(0.0, 0.0, 0.0, 0.2), 10.0, 0.5, 2.0, 2.0);\n    -fx-border-radius: 4.0px;\n    -fx-border-color: linear-gradient(to bottom, rgba(0.0, 0.0, 0.0, 0.3), rgba(0.0, 0.0, 0.0, 0.7));\n    -fx-border-width: 0.5;\n}\n\n.recurrence-popup > .content {\n    -fx-background-color: transparent;\n}\n\n.recurrence-popup > .content > .recurrence-view {\n    -fx-background-color: transparent;\n}\n\n.recurrence-popup > .content > .button-pane {\n    -fx-spacing: 1.0em;\n    -fx-padding: 2.0em 1.0em 0.5em 1.0em;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles used for MonthSheetView control.\n */\n\n.month-sheet-view {\n    -fx-background-color: white;\n}\n\n.month-sheet-view .month-header {\n    -fx-alignment: center;\n    -fx-text-alignment: center;\n    -fx-font-size: 1.1em;\n    -fx-border-color: -today-fill-color;\n    -fx-border-width: 0.0 0.0 2.0 0.0;\n}\n\n.month-sheet-view .date-cell {\n    -fx-padding: 2.0;\n    -fx-border-color: lightgray;\n    -fx-border-width: 0.0 1.0 1.0 0.0;\n    -fx-font-size: .8em;\n}\n\n.month-sheet-view .date-cell:today {\n    -fx-background-color: -today-fill-color;\n}\n\n.month-sheet-view .date-cell .week-number-label {\n    -fx-text-fill: gray;\n    -fx-font-size: 0.7em;\n    -fx-alignment: top-right;\n}\n\n.month-sheet-view .date-cell.weekend-day {\n    -fx-background-color: -weekend-fill-color;\n}\n\n.month-sheet-view > .container > .date-cell .day-of-month-label {\n    -fx-pref-width: 2.0em;\n    -fx-alignment: center;\n    -fx-font-weight: bold;\n}\n\n.month-sheet-view > .container > .date-cell.first-month {\n    -fx-border-width: 0.0 1.0 1.0 1.0;\n}\n\n.month-sheet-view > .container > .date-cell.last-month {\n    -fx-border-width: 0.0 1.0 1.0 0.0;\n}\n\n.month-sheet-view > .container > .date-cell.first-day-of-week {\n}\n\n.month-sheet-view > .container > .date-cell:today > .day-of-month-label,\n.month-sheet-view > .container > .date-cell:today > .day-of-week-label,\n.month-sheet-view > .container > .date-cell:today > .week-number-label {\n    -fx-text-fill: white;\n    -fx-font-weight: bold;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-low {\n    -fx-background-color: -usage-low-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-medium {\n    -fx-background-color: -usage-medium-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-high {\n    -fx-background-color: -usage-high-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-low > .label {\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-low > .label {\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-medium > .label {\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-high > .label {\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.month-sheet-view > .container > .date-cell.usage-very-high > .label {\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell {\n    -fx-min-width: 100.0;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label {\n    -fx-pref-width: 2em;\n    -fx-background-radius: 2;\n    -fx-font-size: 0.8em;\n    -fx-font-weight: bold;\n    -fx-text-alignment: right;\n    -fx-padding: 1.0 4.0 1.0 4.0;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-very-low {\n    -fx-background-color: -usage-very-low-color;\n    -fx-text-fill: -usage-very-low-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-low {\n    -fx-background-color: -usage-low-color;\n    -fx-text-fill: -usage-low-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-medium {\n    -fx-background-color: -usage-medium-color;\n    -fx-text-fill: -usage-medium-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-high {\n    -fx-background-color: -usage-high-color;\n    -fx-text-fill: -usage-high-text-color;\n}\n\n.month-sheet-view > .container > .badge-date-cell > .badge-label.usage-very-high {\n    -fx-background-color: -usage-very-high-color;\n    -fx-text-fill: -usage-very-high-text-color;\n}\n\n.month-sheet-view > .container > .extended-date-cell.weekend-day {\n    -fx-background-color: derive(blanchedalmond, -5.0%);\n}\n\n.month-sheet-view > .container > .extended-date-cell {\n    -fx-background-color: blanchedalmond;\n}\n\n.month-sheet-view > .container > .date-cell:selected {\n    -fx-background-color: -fx-selection-bar;\n    -fx-border-color: derive(-fx-selection-bar, -10.0%);\n}\n\n.month-sheet-view > .container > .date-cell:selected > .day-of-month-label,\n.month-sheet-view > .container > .date-cell:selected > .day-of-week-label,\n.month-sheet-view > .container > .date-cell:selected > .week-number-label {\n    -fx-text-fill: white;\n    -fx-font-weight: bold;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PaperView control.\n */\n\n.paper-view {\n}\n\n.paper-view > .container {\n    -fx-hgap: 10;\n    -fx-vgap: 10;\n}\n\n.paper-view > .container > .custom-fields {\n    -fx-hgap: 5.0;\n    -fx-vgap: 5.0;\n}\n\n.paper-view > .container > .custom-fields > .text-field {\n    -fx-pref-width: 3em;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the TimeRangeView control.\n */\n\n.time-range-view {\n}\n\n.time-range-view > .container {\n    -fx-hgap: 10;\n    -fx-vgap: 10;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the SettingsView control.\n */\n\n.print-settings-view {\n}\n\n.print-settings-view > .container {\n    -fx-spacing: 15.0;\n}\n\n.print-settings-view > .container > .section-title > .label {\n    -fx-font-weight: bold;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the SourceGridView control.\n */\n\n.source-grid-view {\n    -fx-spacing: 7.0;\n}\n\n.source-grid-view .column {\n    -fx-spacing: 5.0;\n}\n\n.source-grid-view .column .item-box {\n    -fx-pref-width: 20.0;\n    -fx-pref-height: 10.0;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PrintablePage control.\n */\n\n.print-page {\n    -fx-background-color: white;\n}\n\n.print-page > .container {\n    -fx-padding: 10.0;\n}\n\n.print-page > .container > .header {\n    -fx-padding: 0.0 0.0 10.0 0.0;\n}\n\n.print-page > .container > .header > .title-section {\n    -fx-spacing: 20.0;\n}\n\n.print-page > .container > .header > .title-section > .period-label {\n    -fx-font-size: 28.0px;\n    -fx-font-weight: bold;\n}\n\n.print-page > .container > .header > .mini-calendars {\n    -fx-padding: 0.0 0.0 0.0 0.0;\n}\n\n.print-page .year-month-view > .container {\n    -fx-padding: 5.0 10.0 5.0 10.0;\n}\n\n/*\n * The YearMonthView instances shown inside the PrintablePage control should be smaller than\n * normally. Otherwise they waste too much space.\n */\n.print-page .year-month-view > .container > .day-of-week-label,\n.print-page .year-month-view > .container > .day-of-month-label,\n.print-page .year-month-view > .container > .day-not-of-month-label {\n    -fx-font-size: 10.0px;\n}\n\n.print-page .year-month-view > .container > .header > .month-header > .month-label,\n.print-page .year-month-view > .container > .header > .year-header > .year-label {\n    -fx-font-size: 14.0px;\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PreviewPane control.\n */\n\n.print-preview {\n    -fx-padding: 10.0;\n}\n\n.print-preview > .container > .center {\n    -fx-background-color: lightgrey;\n    -fx-border-width: 1.0px;\n    -fx-border-color: black;\n}\n\n.print-preview > .container > .footer {\n    -fx-padding: 20.0 0.0 0.0 0.0;\n    -fx-spacing: 10.0;\n}\n\n.print-preview > .container > .center > .zoom-pane > .print-page {\n    -fx-border-color: gray;\n    -fx-border-width: 1.0;\n    -fx-effect: dropshadow(three-pass-box, gray, 10.0, 0.0, 0.0, 0.0);\n}\n\n/* \n * --------------------------------------------------------------------------------------------------------------\n * Styles for the PrintView control.\n */\n\n.print-view {\n    -fx-padding: 15.0;\n    -fx-background-color: white;\n}\n\n.print-view > .container > .button-bar {\n    -fx-alignment: center-right;\n    -fx-spacing: 10;\n}\n\n.print-view > .container > .separator {\n    -fx-padding: 0 10 0 10;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ButtonBar control.\n */\n\n.button-bar > .container > .button {\n    -fx-padding: 3 15 3 15;\n    -fx-border-color: transparent;\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;\n}\n\n.button-bar > .container > .button:focused {\n    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;\n    -fx-background-insets: 0 0 -1 0, 0, 1, 2;\n}\n\n.button-bar > .container > .button.left-pill {\n    -fx-background-radius: 3 0 0 3;\n}\n\n.button-bar > .container > .button.left-pill:focused {\n    -fx-background-radius: 3 0 0 3;\n}\n\n.button-bar > .container > .button.right-pill {\n    -fx-background-radius: 0 3 3 0;\n}\n\n.button-bar > .container > .button.right-pill:focused {\n    -fx-background-radius: 0 3 3 0;\n    -fx-background-insets: -2 -2 -2 0, 0, 1 1 1 0, 2 2 2 1;\n}\n\n.button-bar > .container > .button.center-pill {\n    -fx-background-radius: 0;\n}\n\n.button-bar > .container > .button.center-pill:focused {\n    -fx-background-radius: 0;\n    -fx-background-insets: -2 0 -2 -1, 0 0 0 -1, 1 1 1 0, 2 2 2 1;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the DetailedDayView control.\n */\n\n.detailed-day-view {\n    -fx-padding: 0.0;\n}\n\n.detailed-day-view > .container > .separator {\n    -fx-padding: 5px;\n}\n\n.detailed-day-view > .container > .all-day-label {\n    -fx-font-weight: normal;\n    -fx-text-fill: gray;\n    -fx-font-size: .9em;\n    -fx-border-color: lightgray;\n    -fx-border-width: 0 0 5 0;\n    -fx-padding: 0 2 0 0;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the DetailedWeekView control.\n */\n\n.detailed-week-view > .container > .filler-right,\n.detailed-week-view > .container > .filler-left {\n    -fx-border-color: lightgray;\n    -fx-border-width: 0 0 1 0;\n}\n\n.detailed-week-view > .container > .all-day-filler {\n    -fx-border-color: lightgray;\n    -fx-border-width: 0 0 5 0;\n}\n\n.detailed-week-view > .container > .all-day-label {\n    -fx-font-weight: normal;\n    -fx-text-fill: gray;\n    -fx-font-size: .9em;\n    -fx-border-color: lightgray;\n    -fx-border-width: 0 0 5 0;\n    -fx-padding: 0 2 0 0;\n    -fx-alignment: center-right;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the WeekFieldsView control.\n */\n\n.week-fields {\n}\n\n.week-fields > .content {\n    -fx-hgap: 10;\n    -fx-vgap: 10;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ResourceCalendarView control.\n */\n.resource-calendar-view {\n}\n\n.resource-calendar-view .time-scale {\n    -fx-border-color: lightgrey;\n    -fx-border-width: 0px 1px 0px 0px;\n}\n\n.resource-calendar-view .header-background {\n    -fx-background-color: grey, white;\n    -fx-background-insets: 0, 0 0 1 0;\n}\n\n.resource-calendar-view .marker-line {\n    -fx-pref-height: 5px;\n    -fx-background-color: orange;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the TimeField control.\n */\n.time-field > .box {\n    -fx-spacing: 2px;\n}\n\n/*\n * --------------------------------------------------------------------------------------------------------------\n * Styles for the ResourcesView.\n */\n.resources-view {\n    -fx-border-width: 1px;\n    -fx-border-color: lightgrey;\n}\n\n.resources-view .week-number-label {\n    -fx-font-size: 1.2em;\n    -fx-font-weight: bold;\n}\n\n.resources-view .header-box .day-box .weekday-header-view {\n    -fx-border-color: lightgrey;\n}\n\n.resources-view .header-box .day-box .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header {\n    -fx-padding: 5px;\n    -fx-border-color: lightgrey;\n    -fx-font-weight: bold;\n    -fx-alignment: center;\n}\n\n.resources-view .header-box .day-box .resource-header.first {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header.only,\n.resources-view .header-box .day-box .resource-header.middle {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .day-box .resource-header.last {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n\n.resources-view .header-box .resource-header-view .resource-header {\n    -fx-padding: 5px;\n    -fx-font-weight: bold;\n    -fx-alignment: center;\n}\n\n.resources-view .header-box .resource-header-view .resource-header,\n.resources-view .header-box .resource-header-view .all-day-view,\n.resources-view .header-box .resource-header-view .weekday-header-view {\n    -fx-border-color: lightgrey;\n}\n\n.resources-view .header-box .resource-header-view.first .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.first .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.first .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.last .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .resource-header,\n.resources-view .header-box .resource-header-view.middle .resource-header {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .weekday-header-view,\n.resources-view .header-box .resource-header-view.middle .weekday-header-view {\n    -fx-border-width: 0px 0px 0px 0px;\n}\n\n.resources-view .header-box .resource-header-view.only .all-day-view,\n.resources-view .header-box .resource-header-view.middle .all-day-view {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view {\n    -fx-border-color: lightgrey;\n}\n\n.resources-view .resources-view-container .week-view.first {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view.only {\n    -fx-border-width: 1px;\n}\n\n.resources-view .resources-view-container .week-view.middle {\n    -fx-border-width: 0px 0px 1px 0px;\n}\n\n.resources-view .resources-view-container .week-view.last {\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .upper-left-corner {\n    -fx-border-color: lightgrey;\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .upper-right-corner {\n    -fx-border-color: lightgrey;\n    -fx-border-width: 0px 0px 1px 1px;\n}\n\n.resources-view .time-scale {\n    -fx-border-color: lightgrey;\n    -fx-border-width: 0px 1px 1px 0px;\n}\n\n.resources-view .weekday-separator {\n    -size: 1px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: lightgrey;\n}\n\n.resources-view .large-separator {\n    -size: 4px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: lightgrey;\n}\n\n.resources-view .small-separator {\n    -size: 1px;\n    -fx-pref-width: -size;\n    -fx-min-width: -size;\n    -fx-max-width: -size;\n    -fx-background-color: lightgrey;\n}"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} Days\nAgendaView.MENU_ITEM_LOOK_AHEAD=Look Ahead Period\nAgendaView.MENU_ITEM_LOOK_BACK=Look Back Period\n\nContextMenuProvider.ADD_NEW_EVENT=Add New Event\nContextMenuProvider.DAYS={0} Days\nContextMenuProvider.EARLY_LATE_HOURS=Early / Late Hours\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Compress\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Hide\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Show\nContextMenuProvider.GRID=Grid\nContextMenuProvider.GRID_OFF=Off\nContextMenuProvider.HOURS={0} Hours\nContextMenuProvider.MINUTES={0} Minutes\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Show Days\nContextMenuProvider.SHOW_HOURS=Show Hours\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=Unable to create a new entry. No calendars are currently defined.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Unable to create a new entry. All calendars are either read-only or currently not enabled.{0}{0}Please ensure that at least one calendar is writable and enabled. Then try again double clicking.\nDateControl.DEFAULT_CALENDAR_NAME=Default\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Default\nDateControl.DEFAULT_ENTRY_TITLE=New Entry\nDateControl.DEFAULT_NEW_CALENDAR=Calendar\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Calendar Source\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 Minutes\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 Min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=No calendars defined.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Unable to create new entry.\nDateControl.MENU_CALENDAR=Calendar\nDateControl.MENU_ITEM_DELETE=Delete\nDateControl.MENU_ITEM_INFORMATION=Information\nDateControl.NO_CONTENT=No Content\nDateControl.TITLE_CALENDAR_PROBLEM=Calendar Problem\n\nVirtualGrid.OFF=Off\nVirtualGrid.OFF_SHORT=Off\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=day\nUtil.DAYS=days\nUtil.EVERY_PLURAL=Every {0} {1}\nUtil.EVERY_SINGULAR=Every {0}\nUtil.FIFTH=fifth\nUtil.FIRST=first\nUtil.FOURTH=fourth\nUtil.FRIDAY=Friday\nUtil.HOUR=hour\nUtil.HOURS=hours\nUtil.INVALID_RULE=Invalid recurrence rule.\nUtil.MINUTE=minute\nUtil.MINUTES=minutes\nUtil.MONDAY=Monday\nUtil.MONTH=month\nUtil.MONTH_AND_DAY_FORMAT=MMMM dd\nUtil.MONTHS=months\nUtil.ON_DATE=\\ on {0}\nUtil.ON_MONTH_DAY=\\ on day \nUtil.ON_MONTH_WEEKDAY=\\ on the {0} {1}\nUtil.ON_WEEKDAY=\\ on \nUtil.ONCE=Once\nUtil.SATURDAY=Saturday\nUtil.SECOND=second\nUtil.SECONDS=seconds\nUtil.SUNDAY=Sunday\nUtil.THIRD=third\nUtil.THURSDAY=Thursday\nUtil.TIMES=, {0} times\nUtil.TUESDAY=Tuesday\nUtil.UNTIL_DATE=, until {0}\nUtil.WEDNESDAY=Wednesday\nUtil.WEEK=week\nUtil.WEEKS=weeks\nUtil.YEAR=year\nUtil.YEARS=years\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM yyyy\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Maximize the display of the agenda list\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Maximize the display of the day's schedule\nDayPage.TOOLTIP_STANDARD_LAYOUT=Show agenda list and day's schedule\nDayPage.TOOLTIP_LAYOUT=Toggle calendar layout\n\nMonthPage.DATE_FORMAT=MMMM yyyy\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Toggle calendar layout\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Grid / Column View\n\nEntriesPane.FULL_DAY=Full Day\nEntryDetailsView.CUSTOM=Custom\nEntryDetailsView.DAILY=Daily\nEntryDetailsView.FROM=From:\nEntryDetailsView.FULL_DAY=Full Day:\nEntryDetailsView.MENU_BUTTON_NONE=None\nEntryDetailsView.MENU_ITEM_CUSTOM=Custom...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Every Day\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Every Month\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Every Week\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Every Year\nEntryDetailsView.MENU_ITEM_NONE=None\nEntryDetailsView.MONTHLY=Monthly\nEntryDetailsView.NONE=None\nEntryDetailsView.REPEAT=Repeat:\nEntryDetailsView.TIMEZONE=Timezone:\nEntryDetailsView.TO=To:\nEntryDetailsView.WEEKLY=Weekly\nEntryDetailsView.YEARLY=Yearly\nEntryHeaderView.PROMPT_LOCATION=Add Location\nEntryHeaderView.PROMPT_TITLE=Title\n\nEntryPopOverContentPane.DETAILS=Details\n\nRecurrencePopupSkin.CANCEL=Cancel\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=All Day\nPageBaseSkin.TODAY=Today\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE={0} to {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, {1} to {2}, {3}\nAgendaEntryCell.ALL_DAY=all-day\n\nAgendaViewSkin.NO_ENTRIES=No Entries\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda for {0} until {1}\n\nMonthEntryViewSkin.ENDS_AT=ends at {0}\nMonthViewSkin.MORE_ENTRIES={0} more...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=Weeks\nRecurrenceViewSkin.AFTER=After\nRecurrenceViewSkin.DAILY=Daily\nRecurrenceViewSkin.DAY_OF_MONTH=Day of the Month\nRecurrenceViewSkin.DAY_OF_WEEK=Day of the Week\nRecurrenceViewSkin.DAYS=Days\nRecurrenceViewSkin.ENDS=Ends:\nRecurrenceViewSkin.FREQUENCY=Frequency:\nRecurrenceViewSkin.MONTHLY=Monthly\nRecurrenceViewSkin.MONTHS=Months\nRecurrenceViewSkin.NEVER=Never\nRecurrenceViewSkin.OCCURENCES=occurences\nRecurrenceViewSkin.ON=On\nRecurrenceViewSkin.REPEAT_BY=Repeat by:\nRecurrenceViewSkin.REPEAT_EVERY=Repeat every:\nRecurrenceViewSkin.REPEAT_ON=Repeat on:\nRecurrenceViewSkin.SHORT_FRIDAY=F\nRecurrenceViewSkin.SHORT_MONDAY=M\nRecurrenceViewSkin.SHORT_SATURDAY=S\nRecurrenceViewSkin.SHORT_SUNDAY=S\nRecurrenceViewSkin.SHORT_THURSDAY=T\nRecurrenceViewSkin.SHORT_TUESDAY=T\nRecurrenceViewSkin.SHORT_WEDNESDAY=W\nRecurrenceViewSkin.STARTS_ON=Starts on:\nRecurrenceViewSkin.SUMMARY=Summary:\nRecurrenceViewSkin.WEEKLY=Weekly\nRecurrenceViewSkin.YEARLY=Yearly\nRecurrenceViewSkin.YEARS=Years\n\nSearchResultViewSkin.FROM_UNTIL={0} to {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, {1} to {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=All Day\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Today\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Calendars\nCalendarViewSkin.TOGGLE_SHOW_DAY=Day\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Show a week\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Show a month\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Show a year\nCalendarViewSkin.PROMPT_SEARCH_FIELD=Search\nCalendarViewSkin.TOGGLE_SHOW_WEEK=Week\nCalendarViewSkin.TOGGLE_SHOW_MONTH=Month\nCalendarViewSkin.TOGGLE_SHOW_YEAR=Year\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Show list of all calendars\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Add a calendar\nCalendarViewSkin.TOOLTIP_PRINT=Print\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Show a day\n\nPrintViewType.DAY_VIEW=Day\nPrintViewType.DAY_SINGULAR_CHRONO=day\nPrintViewType.DAY_PLURAL_CHRONO=days\nPrintViewType.WEEK_VIEW=Week\nPrintViewType.WEEK_SINGULAR_CHRONO=week\nPrintViewType.WEEK_PLURAL_CHRONO=weeks\nPrintViewType.MONTH_VIEW=Month\nPrintViewType.MONTH_SINGULAR_CHRONO=month\nPrintViewType.MONTH_PLURAL_CHRONO=months\nPrintViewType.TO_LABEL=to\nPrintViewType.PAPER_TITLE_LABEL=Paper\nPrintViewType.TIME_RANGE_TITLE_LABEL=Time Range\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Calendars\nPrintViewType.OPTIONS_TITLE_LABEL=Options\n\nPaperViewSkin.VIEW_TYPE_LABEL=View\nPaperViewSkin.PAPER_LABEL=Paper\nPaperViewSkin.MARGIN_LABEL=Margin\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=All Day Entries\nOptionsViewSkin.DETAILS_LABEL=Details\nOptionsViewSkin.TIMED_EVENTS_LABEL=Timed Entries\nOptionsViewSkin.MINI_CALENDAR_LABEL=Mini Calendar\nOptionsViewSkin.CALENDAR_KEYS_LABEL=Calendar Keys\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Column Layout\n\nTimeRangeViewSkin.START_LABEL=Start\nTimeRangeViewSkin.END_LABEL=End\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR={0} {1} will be printed\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL={0} {1} will be printed\n\nTimeRangeFieldValue.TODAY_LABEL=Today\nTimeRangeFieldValue.TOMORROW=Tomorrow\nTimeRangeFieldValue.ON_DATE_LABEL=On Date\nTimeRangeFieldValue.THIS_WEEK_LABEL=This Week\nTimeRangeFieldValue.NEXT_WEEK_LABEL=Next Week\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=On Week\nTimeRangeFieldValue.THIS_MONTH_LABEL=This Month\nTimeRangeFieldValue.NEXT_MONTH_LABEL=Next Month\nTimeRangeFieldValue.JANUARY_LABEL=January\nTimeRangeFieldValue.FEBRUARY_LABEL=February\nTimeRangeFieldValue.MARCH_LABEL=March\nTimeRangeFieldValue.APRIL_LABEL=April\nTimeRangeFieldValue.MAY_LABEL=May\nTimeRangeFieldValue.JUNE_LABEL=June\nTimeRangeFieldValue.JULY_LABEL=July\nTimeRangeFieldValue.AUGUST_LABEL=August\nTimeRangeFieldValue.SEPTEMBER_LABEL=September\nTimeRangeFieldValue.OCTOBER_LABEL=October\nTimeRangeFieldValue.NOVEMBER_LABEL=November\nTimeRangeFieldValue.DECEMBER_LABEL=December\nTimeRangeFieldValue.AFTER_LABEL=After\n\nPreviewPaneSkin.ZOOM_LABEL=Zoom\n\nPrintView.TITLE_LABEL=Print\nPrintView.CONTINUE_BUTTON=Continue\nPrintView.CANCEL_BUTTON=Cancel\nPrintView.NO_PRINTERS = No printers available.\nPrintView.ERROR_NO_PRINTER = It is not possible to print because there is no printer service installed on this system.\n\nMargin.DEFAULT=Default\nMargin.MINIMUM=Minimum\nMargin.CUSTOM=Custom\n\nMarginSelector.TOP=Top\nMarginSelector.RIGHT=Right\nMarginSelector.BOTTOM=Bottom\nMarginSelector.LEFT=Left\n\nSourceView.DISABLE_ALL=Disable all\nSourceView.ENABLE_ALL=Enable all\n\nMonthSheetView.ADD_NEW_EVENT=Add New Event\nMonthSheetView.STANDARD_CELLS=Standard\nMonthSheetView.USAGE_CELLS=Usage\nMonthSheetView.DETAIL_CELLS=Detail\nMonthSheetView.BADGE_CELLS=Counter"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_de.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} Tage\nAgendaView.MENU_ITEM_LOOK_AHEAD=Vorausschau\nAgendaView.MENU_ITEM_LOOK_BACK=R\\u00FCckschau\n\nContextMenuProvider.ADD_NEW_EVENT=Neues Ereignis\nContextMenuProvider.DAYS={0} Tage\nContextMenuProvider.EARLY_LATE_HOURS=Fr\\u00FChe / Sp\\u00E4te Stunden\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Verkleinert\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Verbergen\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Anzeigen\nContextMenuProvider.GRID=Gitter\nContextMenuProvider.GRID_OFF=Aus\nContextMenuProvider.HOURS={0} Stunden\nContextMenuProvider.MINUTES={0} Minuten\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Tage\nContextMenuProvider.SHOW_HOURS=Stunden\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=Ein neues Ereignis kann nicht angelegt werden. Es existieren keine Kalender daf\\u00FCr.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Ein neues Ereignis kann nicht angelegt werden. Alle Kalender sind entweder schreibgesch\\u00FCtzt oder ausgeschaltet.{0}{0}Es muss sicher gestellt werden, dass mindestens ein Kalender ver\\u00E4nderbar und eingeschaltet ist.\nDateControl.DEFAULT_CALENDAR_NAME=Standard\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Standard\nDateControl.DEFAULT_ENTRY_TITLE=Neues Ereignis\nDateControl.DEFAULT_NEW_CALENDAR=Kalender\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Kalender Quelle\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 Minuten\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 Min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=Keine Kalender definiert.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Neues Ereignis kann nicht angelegt werden.\nDateControl.MENU_CALENDAR=Kalender\nDateControl.MENU_ITEM_DELETE=L\\u00F6schen\nDateControl.MENU_ITEM_INFORMATION=Informationen\nDateControl.NO_CONTENT=Kein Inhalt\nDateControl.TITLE_CALENDAR_PROBLEM=Kalender Probleme\n\nVirtualGrid.OFF=Aus\nVirtualGrid.OFF_SHORT=Aus\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=Tag\nUtil.DAYS=Tage\nUtil.EVERY_PLURAL=Alle {0} {1}\nUtil.EVERY_SINGULAR=Alle {0}\nUtil.FIFTH=f\\u00FCnfte\nUtil.FIRST=erste\nUtil.FOURTH=vierte\nUtil.FRIDAY=Freitag\nUtil.HOUR=Stunde\nUtil.HOURS=Stunden\nUtil.INVALID_RULE=Ung\\u00FCltige Regel\nUtil.MINUTE=Minute\nUtil.MINUTES=Minuten\nUtil.MONDAY=Montag\nUtil.MONTH=Monat\nUtil.MONTH_AND_DAY_FORMAT=dd. MMM\nUtil.MONTHS=Monate\nUtil.ON_DATE=\\\\ am {0}\nUtil.ON_MONTH_DAY=\\\\ an Tag \nUtil.ON_MONTH_WEEKDAY=\\\\ an dem {0} {1}\nUtil.ON_WEEKDAY=\\\\ am \nUtil.ONCE=Einmal\nUtil.SATURDAY=Samstag\nUtil.SECOND=Sekunde\nUtil.SECONDS=Sekunden\nUtil.SUNDAY=Sonntag\nUtil.THIRD=dritte\nUtil.THURSDAY=Donnerstag\nUtil.TIMES=, {0} mal\nUtil.TUESDAY=Dienstag\nUtil.UNTIL_DATE=, bis {0}\nUtil.WEDNESDAY=Mittwoch\nUtil.WEEK=Woche\nUtil.WEEKS=Wochen\nUtil.YEAR=Jahr\nUtil.YEARS=Jahre\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM yyyy\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Agenda Ansicht maximieren\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Tagesansicht maximieren\nDayPage.TOOLTIP_STANDARD_LAYOUT=Agenda und Tagesansicht zusammen anzeigen\nDayPage.TOOLTIP_LAYOUT=Layout \\u00E4ndern\n\nMonthPage.DATE_FORMAT=MMMM yyyy\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Layout \\u00E4ndern\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Gitter / Spalten Ansicht\n\nEntriesPane.FULL_DAY=Ganzt\\u00E4gig\nEntryDetailsView.CUSTOM=Eigene\nEntryDetailsView.DAILY=T\\u00E4glich\nEntryDetailsView.FROM=Von:\nEntryDetailsView.FULL_DAY=Ganzt\\u00E4gig:\nEntryDetailsView.MENU_BUTTON_NONE=Keine\nEntryDetailsView.MENU_ITEM_CUSTOM=Eigene...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Jeden Tag\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Jeden Monat\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Jede Woche\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Jedes Jahr\nEntryDetailsView.MENU_ITEM_NONE=Ohne\nEntryDetailsView.MONTHLY=Monatlich\nEntryDetailsView.NONE=Ohne\nEntryDetailsView.REPEAT=Wiederholen:\nEntryDetailsView.TIMEZONE=Zeitzone:\nEntryDetailsView.TO=Bis:\nEntryDetailsView.WEEKLY=W\\u00F6chentlich\nEntryDetailsView.YEARLY=J\\u00E4hrlich\nEntryHeaderView.PROMPT_LOCATION=Ort hinzuf\\u00FCgen\nEntryHeaderView.PROMPT_TITLE=Titel\n\nEntryPopOverContentPane.DETAILS=Details\n\nRecurrencePopupSkin.CANCEL=Abbruch\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=Ganzt\\u00E4gig\nPageBaseSkin.TODAY=Heute\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE={0} bis {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, {1} bis {2}, {3}\nAgendaEntryCell.ALL_DAY=Ganzt\\u00E4gig\n\nAgendaViewSkin.NO_ENTRIES=Keine Ereignisse\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda f\\u00FCr den {0} bis zum {1}\n\nMonthEntryViewSkin.ENDS_AT=endet um {0}\nMonthViewSkin.MORE_ENTRIES={0} mehr...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=Wochen\nRecurrenceViewSkin.AFTER=Nach\nRecurrenceViewSkin.DAILY=T\\u00E4glich\nRecurrenceViewSkin.DAY_OF_MONTH=Tag des Monats\nRecurrenceViewSkin.DAY_OF_WEEK=Tag der Woche\nRecurrenceViewSkin.DAYS=Tage\nRecurrenceViewSkin.ENDS=Endet:\nRecurrenceViewSkin.FREQUENCY=Wiederholung:\nRecurrenceViewSkin.MONTHLY=Monatlich\nRecurrenceViewSkin.MONTHS=Monate\nRecurrenceViewSkin.NEVER=Niemals\nRecurrenceViewSkin.OCCURENCES=Wiederholungen\nRecurrenceViewSkin.ON=Am\nRecurrenceViewSkin.REPEAT_BY=Wiederholen am:\nRecurrenceViewSkin.REPEAT_EVERY=Wiederholen nach:\nRecurrenceViewSkin.REPEAT_ON=Wiederholen am:\nRecurrenceViewSkin.SHORT_FRIDAY=F\nRecurrenceViewSkin.SHORT_MONDAY=M\nRecurrenceViewSkin.SHORT_SATURDAY=S\nRecurrenceViewSkin.SHORT_SUNDAY=S\nRecurrenceViewSkin.SHORT_THURSDAY=D\nRecurrenceViewSkin.SHORT_TUESDAY=D\nRecurrenceViewSkin.SHORT_WEDNESDAY=M\nRecurrenceViewSkin.STARTS_ON=Beginn am:\nRecurrenceViewSkin.SUMMARY=Zusammenfassung:\nRecurrenceViewSkin.WEEKLY=W\\u00F6chentlich\nRecurrenceViewSkin.YEARLY=J\\u00E4hrlich\nRecurrenceViewSkin.YEARS=Jahre\n\nSearchResultViewSkin.FROM_UNTIL={0} bis {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, {1} bis {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=Ganzt\\u00E4gig\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Heute\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Kalender\nCalendarViewSkin.TOGGLE_SHOW_DAY=Tag\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Ganze Woche anzeigen\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Ganzen Monat anzeigen\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Ganzes Jahr anzeigen\nCalendarViewSkin.PROMPT_SEARCH_FIELD=Suchen\nCalendarViewSkin.TOGGLE_SHOW_WEEK=Woche\nCalendarViewSkin.TOGGLE_SHOW_MONTH=Monat\nCalendarViewSkin.TOGGLE_SHOW_YEAR=Jahr\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Liste aller verf\\u00FCgbaren Kalender anzeigen\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Kalender hinzuf\\u00FCgen\nCalendarViewSkin.TOOLTIP_PRINT=Drucken\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Einzelnen Tag anzeigen\n\nPrintViewType.DAY_VIEW=Tag\nPrintViewType.DAY_SINGULAR_CHRONO=Tage\nPrintViewType.DAY_PLURAL_CHRONO=Tage\nPrintViewType.WEEK_VIEW=Woche\nPrintViewType.WEEK_SINGULAR_CHRONO=Wochen\nPrintViewType.WEEK_PLURAL_CHRONO=Wochen\nPrintViewType.MONTH_VIEW=Monat\nPrintViewType.MONTH_SINGULAR_CHRONO=Monate\nPrintViewType.MONTH_PLURAL_CHRONO=Monate\nPrintViewType.TO_LABEL=bis\nPrintViewType.PAPER_TITLE_LABEL=Papier\nPrintViewType.TIME_RANGE_TITLE_LABEL=Zeitspanne\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Kalender\nPrintViewType.OPTIONS_TITLE_LABEL=Optionen\n\nPaperViewSkin.VIEW_TYPE_LABEL=Ansicht\nPaperViewSkin.PAPER_LABEL=Papier\nPaperViewSkin.MARGIN_LABEL=R\\u00E4nder\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=Ganzt\\u00E4gige Ereignisse\nOptionsViewSkin.DETAILS_LABEL=Details\nOptionsViewSkin.TIMED_EVENTS_LABEL=Ereignisse\nOptionsViewSkin.MINI_CALENDAR_LABEL=Mini-Kalender\nOptionsViewSkin.CALENDAR_KEYS_LABEL=Kalender-Legende\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Spaltenansicht\n\nTimeRangeViewSkin.START_LABEL=Start\nTimeRangeViewSkin.END_LABEL=Ende\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR={0} {1} wird gedruckt\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL={0} {1} wird gedruckt\n\nTimeRangeFieldValue.TODAY_LABEL=Heute\nTimeRangeFieldValue.TOMORROW=Morgen\nTimeRangeFieldValue.ON_DATE_LABEL=Datum\nTimeRangeFieldValue.THIS_WEEK_LABEL=Diese Woche\nTimeRangeFieldValue.NEXT_WEEK_LABEL=N\\u00E4chste Woche\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=Kalenderwoche\nTimeRangeFieldValue.THIS_MONTH_LABEL=Diesen Monat\nTimeRangeFieldValue.NEXT_MONTH_LABEL=N\\u00E4chster Monat\nTimeRangeFieldValue.JANUARY_LABEL=Januar\nTimeRangeFieldValue.FEBRUARY_LABEL=Februar\nTimeRangeFieldValue.MARCH_LABEL=M\\u00E4rz\nTimeRangeFieldValue.APRIL_LABEL=April\nTimeRangeFieldValue.MAY_LABEL=Mai\nTimeRangeFieldValue.JUNE_LABEL=Juni\nTimeRangeFieldValue.JULY_LABEL=Juli\nTimeRangeFieldValue.AUGUST_LABEL=August\nTimeRangeFieldValue.SEPTEMBER_LABEL=September\nTimeRangeFieldValue.OCTOBER_LABEL=Oktober\nTimeRangeFieldValue.NOVEMBER_LABEL=November\nTimeRangeFieldValue.DECEMBER_LABEL=Dezember\nTimeRangeFieldValue.AFTER_LABEL=Nach\n\nPreviewPaneSkin.ZOOM_LABEL=Zoom\n\nPrintView.TITLE_LABEL=Drucken\nPrintView.CONTINUE_BUTTON=Weiter\nPrintView.CANCEL_BUTTON=Abbruch\nPrintView.NO_PRINTERS = Keine Drucker verf\\u00FCgbar.\nPrintView.ERROR_NO_PRINTER = Das Drucken ist nicht m\\u00F6glich, da kein Druckerdienst im System installiert ist.\n\nMargin.DEFAULT=Standardeinstellung\nMargin.MINIMUM=Minimum\nMargin.CUSTOM=Benutzerdefiniert\n\nMarginSelector.TOP=Oben\nMarginSelector.RIGHT=Rechts\nMarginSelector.BOTTOM=Unten\nMarginSelector.LEFT=Links\n\nSourceView.DISABLE_ALL=Alle deaktivieren\nSourceView.ENABLE_ALL=Alle aktivieren\n\nMonthSheetView.ADD_NEW_EVENT=Neues Ereignis\nMonthSheetView.STANDARD_CELLS=Standard\nMonthSheetView.USAGE_CELLS=Auslastung\nMonthSheetView.DETAIL_CELLS=Details\nMonthSheetView.BADGE_CELLS=Z\\u00E4hler"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_es.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} D\\u00EDas\nAgendaView.MENU_ITEM_LOOK_AHEAD=Per\\u00EDodos Posteriores\nAgendaView.MENU_ITEM_LOOK_BACK=Per\\u00EDodos Previos\n\nContextMenuProvider.ADD_NEW_EVENT=Agregar Nuevo Evento\nContextMenuProvider.DAYS={0} D\\u00EDas\nContextMenuProvider.EARLY_LATE_HOURS=Horas de la Madrugada/Noche\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Comprimir\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Ocultar\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Mostrar\nContextMenuProvider.GRID=Cuadr\\u00EDcula\nContextMenuProvider.GRID_OFF=Deshabilitar\nContextMenuProvider.HOURS={0} Horas\nContextMenuProvider.MINUTES={0} Minutos\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Mostrar D\\u00EDas\nContextMenuProvider.SHOW_HOURS=Mostrar Horas\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=No se puede crear un nuevo evento. No hay calendarios actualmente definidos.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=No se puede crear un nuevo evento. Todos los calendarios son de solo lectura o actualmente no est\\u00E1n habilitados.{0}{0}Aseg\\u00FArese de que al menos un calendario tenga permisos de escritura y est\\u00E9 habilitado. Luego intente de nuevo haciendo doble clic.\nDateControl.DEFAULT_CALENDAR_NAME=Predeterminado\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Predeterminado\nDateControl.DEFAULT_ENTRY_TITLE=Nuevo Evento\nDateControl.DEFAULT_NEW_CALENDAR=Calendario\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Calendarios\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 Minutos\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 Min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=No hay Calendarios Definidos.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=No se puede crear un nuevo evento.\nDateControl.MENU_CALENDAR=Calendario\nDateControl.MENU_ITEM_DELETE=Eliminar\nDateControl.MENU_ITEM_INFORMATION=Informaci\\u00F3n\nDateControl.NO_CONTENT=Sin contenido\nDateControl.TITLE_CALENDAR_PROBLEM=Problema en el Calendario\n\nVirtualGrid.OFF=Deshabilitar\nVirtualGrid.OFF_SHORT=Deshabilitar\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=d\\u00EDa\nUtil.DAYS=d\\u00EDas\nUtil.EVERY_PLURAL=Cada {0} {1}\nUtil.EVERY_SINGULAR=Cada {0}\nUtil.FIFTH=quinto\nUtil.FIRST=primero\nUtil.FOURTH=cuarto\nUtil.FRIDAY=Viernes\nUtil.HOUR=hora\nUtil.HOURS=horas\nUtil.INVALID_RULE=Regla de recurrencia no v\\u00E1lida.\nUtil.MINUTE=minuto\nUtil.MINUTES=minutos\nUtil.MONDAY=Lunes\nUtil.MONTH=mes\nUtil.MONTH_AND_DAY_FORMAT=MMMM dd\nUtil.MONTHS=meses\nUtil.ON_DATE=\\ en {0}\nUtil.ON_MONTH_DAY=\\ el d\\u00EDa \nUtil.ON_MONTH_WEEKDAY=\\ en la {0} {1}\nUtil.ON_WEEKDAY=\\ en \nUtil.ONCE=Una vez\nUtil.SATURDAY=S\\u00E1bado\nUtil.SECOND=segundo\nUtil.SECONDS=segundos\nUtil.SUNDAY=Domingo\nUtil.THIRD=tercero\nUtil.THURSDAY=Jueves\nUtil.TIMES=, {0} veces\nUtil.TUESDAY=Martes\nUtil.UNTIL_DATE=, hasta {0}\nUtil.WEDNESDAY=Mi\\u00E9rcoles\nUtil.WEEK=semana\nUtil.WEEKS=semanas\nUtil.YEAR=a\\u00F1o\nUtil.YEARS=a\\u00F1os\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM yyyy\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Maximice la visualizaci\\u00F3n de la agenda\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Maximice la visualizaci\\u00F3n del horario\nDayPage.TOOLTIP_STANDARD_LAYOUT=Visualice la agenda y el horario\nDayPage.TOOLTIP_LAYOUT=Agrupar Calendar\n\nMonthPage.DATE_FORMAT=MMMM yyyy\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Agrupar Calendarios\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Vista Cuadricula / Columnas\n\nEntriesPane.FULL_DAY=Todo el d\\u00EDa\nEntryDetailsView.CUSTOM=Perzonalizado\nEntryDetailsView.DAILY=Diario\nEntryDetailsView.FROM=De:\nEntryDetailsView.FULL_DAY=Todo el d\\u00EDa:\nEntryDetailsView.MENU_BUTTON_NONE=Ninguno\nEntryDetailsView.MENU_ITEM_CUSTOM=Perzonalizado...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Cada D\\u00EDa\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Cada Mes\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Cada Semana\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Cada A\\u00F1o\nEntryDetailsView.MENU_ITEM_NONE=Ninguna\nEntryDetailsView.MONTHLY=Mensual\nEntryDetailsView.NONE=Ninguno\nEntryDetailsView.REPEAT=Repetir:\nEntryDetailsView.TIMEZONE=Zona horaria:\nEntryDetailsView.TO=A:\nEntryDetailsView.WEEKLY=Semanal\nEntryDetailsView.YEARLY=Anual\nEntryHeaderView.PROMPT_LOCATION=Agregar Ubicaci\\u00F3n\nEntryHeaderView.PROMPT_TITLE=T\\u00EDtulo\n\nEntryPopOverContentPane.DETAILS=Detalles\n\nRecurrencePopupSkin.CANCEL=Cancelar\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=Todo el d\\u00EDa\nPageBaseSkin.TODAY=Hoy\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE={0} a {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, {1} a {2}, {3}\nAgendaEntryCell.ALL_DAY=todo el d\\u00EDa\n\nAgendaViewSkin.NO_ENTRIES=Sin Eventos\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda para {0} hasta {1}\n\nMonthEntryViewSkin.ENDS_AT=termina el {0}\nMonthViewSkin.MORE_ENTRIES={0} m\\u00E1s...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=Semanas\nRecurrenceViewSkin.AFTER=Despu\\u00E9s\nRecurrenceViewSkin.DAILY=Diario\nRecurrenceViewSkin.DAY_OF_MONTH=D\\u00EDa de el Mes\nRecurrenceViewSkin.DAY_OF_WEEK=D\\u00EDa de la Semana\nRecurrenceViewSkin.DAYS=D\\u00EDas\nRecurrenceViewSkin.ENDS=Finaliza:\nRecurrenceViewSkin.FREQUENCY=Frecuencia:\nRecurrenceViewSkin.MONTHLY=Mensual\nRecurrenceViewSkin.MONTHS=Meses\nRecurrenceViewSkin.NEVER=Nunca\nRecurrenceViewSkin.OCCURENCES=ocurrencias\nRecurrenceViewSkin.ON=Activo\nRecurrenceViewSkin.REPEAT_BY=Repetir por:\nRecurrenceViewSkin.REPEAT_EVERY=Repetir cada:\nRecurrenceViewSkin.REPEAT_ON=Repetir en:\nRecurrenceViewSkin.SHORT_FRIDAY=V\nRecurrenceViewSkin.SHORT_MONDAY=L\nRecurrenceViewSkin.SHORT_SATURDAY=S\nRecurrenceViewSkin.SHORT_SUNDAY=D\nRecurrenceViewSkin.SHORT_THURSDAY=J\nRecurrenceViewSkin.SHORT_TUESDAY=M\nRecurrenceViewSkin.SHORT_WEDNESDAY=M\nRecurrenceViewSkin.STARTS_ON=Inicia en:\nRecurrenceViewSkin.SUMMARY=Resumen:\nRecurrenceViewSkin.WEEKLY=Semanal\nRecurrenceViewSkin.YEARLY=Anual\nRecurrenceViewSkin.YEARS=a\\u00F1os\n\nSearchResultViewSkin.FROM_UNTIL={0} a {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, {1} a {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=Todo el d\\u00EDa\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Hoy\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Calendarios\nCalendarViewSkin.TOGGLE_SHOW_DAY=D\\u00EDa\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Mostrar una semana\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Mostrar un mes\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Mostrar un a\\u00F1o\nCalendarViewSkin.PROMPT_SEARCH_FIELD=Buscar\nCalendarViewSkin.TOGGLE_SHOW_WEEK=Semana\nCalendarViewSkin.TOGGLE_SHOW_MONTH=Mes\nCalendarViewSkin.TOGGLE_SHOW_YEAR=A\\u00F1o\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Mostrar lista de Calendarios\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Agregar un Calendario\nCalendarViewSkin.TOOLTIP_PRINT=Imprimir\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Mostrar un d\\u00EDa\n\nPrintViewType.DAY_VIEW=D\\u00EDa\nPrintViewType.DAY_SINGULAR_CHRONO=d\\u00EDa \nPrintViewType.DAY_PLURAL_CHRONO=d\\u00EDas \nPrintViewType.WEEK_VIEW=Semana\nPrintViewType.WEEK_SINGULAR_CHRONO=semana \nPrintViewType.WEEK_PLURAL_CHRONO=semanas \nPrintViewType.MONTH_VIEW=Mes\nPrintViewType.MONTH_SINGULAR_CHRONO=mes \nPrintViewType.MONTH_PLURAL_CHRONO=meses\nPrintViewType.TO_LABEL=a\nPrintViewType.PAPER_TITLE_LABEL=Papel\nPrintViewType.TIME_RANGE_TITLE_LABEL=Rango\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Calendarios\nPrintViewType.OPTIONS_TITLE_LABEL=Opciones\n\nPaperViewSkin.VIEW_TYPE_LABEL=Vista\nPaperViewSkin.PAPER_LABEL=Papel\nPaperViewSkin.MARGIN_LABEL=Margen\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=Eventos de todo el d\\u00EDa\nOptionsViewSkin.DETAILS_LABEL=Detalles\nOptionsViewSkin.TIMED_EVENTS_LABEL=Eventos Programados\nOptionsViewSkin.MINI_CALENDAR_LABEL=Minicalendario\nOptionsViewSkin.CALENDAR_KEYS_LABEL=Leyendas del Calendario\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Agrupar por Calendario\n\nTimeRangeViewSkin.START_LABEL=Inicio\nTimeRangeViewSkin.END_LABEL=Fin\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR=Se imprimir\\u00E1 {0} {1} \nTimeRangeViewSkin.PERIOD_LABEL_PLURAL=Se imprimir\\u00E1n {0} {1} \n\nTimeRangeFieldValue.TODAY_LABEL=Hoy\nTimeRangeFieldValue.TOMORROW=Ma\\u00F1ana\nTimeRangeFieldValue.ON_DATE_LABEL=En la fecha\nTimeRangeFieldValue.THIS_WEEK_LABEL=Esta semana\nTimeRangeFieldValue.NEXT_WEEK_LABEL=Siguiente Semana\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=En la Semana\nTimeRangeFieldValue.THIS_MONTH_LABEL=Este Mes\nTimeRangeFieldValue.NEXT_MONTH_LABEL=Siguiente Mes\nTimeRangeFieldValue.JANUARY_LABEL=Enero\nTimeRangeFieldValue.FEBRUARY_LABEL=Febrero\nTimeRangeFieldValue.MARCH_LABEL=Marzo\nTimeRangeFieldValue.APRIL_LABEL=Abril\nTimeRangeFieldValue.MAY_LABEL=Mayo\nTimeRangeFieldValue.JUNE_LABEL=Junio\nTimeRangeFieldValue.JULY_LABEL=Julio\nTimeRangeFieldValue.AUGUST_LABEL=Agosto\nTimeRangeFieldValue.SEPTEMBER_LABEL=Septiembre\nTimeRangeFieldValue.OCTOBER_LABEL=Octubre\nTimeRangeFieldValue.NOVEMBER_LABEL=Noviembre\nTimeRangeFieldValue.DECEMBER_LABEL=Diciembre\nTimeRangeFieldValue.AFTER_LABEL=Despu\\u00E9s\n\nPreviewPaneSkin.ZOOM_LABEL=Zoom\n\nPrintView.TITLE_LABEL=Imprimir\nPrintView.CONTINUE_BUTTON=Continuar\nPrintView.CANCEL_BUTTON=Cancelar\nPrintView.NO_PRINTERS = No hay impresoras disponibles.\nPrintView.ERROR_NO_PRINTER = No es posible imprimir porque no hay un servicio de impresora instalado en el sistema.\n\nMargin.DEFAULT=Predeterminado\nMargin.MINIMUM=M\\u00EDnimo\nMargin.CUSTOM=Personalizado\n\nMarginSelector.TOP=Superior\nMarginSelector.RIGHT=Derecha\nMarginSelector.BOTTOM=Inferior\nMarginSelector.LEFT=Izquierda\n\nSourceView.DISABLE_ALL=Desactivar todo\nSourceView.ENABLE_ALL=Activar todo\n\nMonthSheetView.ADD_NEW_EVENT=Agregar Nuevo Evento\nMonthSheetView.STANDARD_CELLS=Estandar\nMonthSheetView.USAGE_CELLS=Uso\nMonthSheetView.DETAIL_CELLS=Detalle\nMonthSheetView.BADGE_CELLS=Contador"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_fr.properties",
    "content": "#Generated by Eclipse Messages Editor (Eclipse Babel)\r\n\r\nAgendaEntryCell.ALL_DAY                    = toute la journ\\u00E9e\r\nAgendaEntryCell.ENTRY_TIME_RANGE           = De {0} \\u00E0 {1}\r\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE = De {0}, {1} \\u00E0 {2}, {3}\r\nAgendaEntryCell.WEEKDAY_FORMAT             = EEEE\r\n\r\nAgendaView.MENU_ITEM_DAYS        = {0} Jours\r\nAgendaView.MENU_ITEM_LOOK_AHEAD  = A aujourd'hui moins  \r\nAgendaView.MENU_ITEM_LOOK_BACK   = De aujourd'hui plus \r\n\r\nAgendaViewSkin.AGENDA_TIME_RANGE = Agenda du {0} au {1}\r\nAgendaViewSkin.NO_ENTRIES        = Pas d'\\u00E9v\\u00E9nements\r\n\r\nCalendarViewSkin.PROMPT_SEARCH_FIELD  = Recherche\r\nCalendarViewSkin.TOGGLE_SHOW_DAY      = Jour\r\nCalendarViewSkin.TOGGLE_SHOW_MONTH    = Mois\r\nCalendarViewSkin.TOGGLE_SHOW_WEEK     = Semaine\r\nCalendarViewSkin.TOGGLE_SHOW_YEAR     = Ann\\u00E9e\r\nCalendarViewSkin.TOGGLE_SOURCE_TRAY   = Calendriers\r\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR = Ajouter un calendrier\r\nCalendarViewSkin.TOOLTIP_PRINT        = Imprimer\r\nCalendarViewSkin.TOOLTIP_SHOW_DAY     = Afficher une journ\\u00E9e\r\nCalendarViewSkin.TOOLTIP_SHOW_MONTH   = Afficher un mois\r\nCalendarViewSkin.TOOLTIP_SHOW_WEEK    = Afficher une semaine\r\nCalendarViewSkin.TOOLTIP_SHOW_YEAR    = Afficher une ann\\u00E9e\r\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY  = Afficher la liste des calendriers\r\n\r\nContextMenuProvider.ADD_NEW_EVENT               = Ajouter un \\\r\n                                                  \\u00E9v\\u00E8nement\r\nContextMenuProvider.DAYS                        = {0} Jours\r\nContextMenuProvider.EARLY_LATE_HOURS            = Premi\\u00E8re / \\\r\n                                                  Derni\\u00E8re Heure\r\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED = Compresser\r\nContextMenuProvider.EARLY_LATE_HOURS_HIDE       = Cacher\r\nContextMenuProvider.EARLY_LATE_HOURS_SHOW       = Afficher\r\nContextMenuProvider.GRID                        = Grille\r\nContextMenuProvider.GRID_OFF                    = Inactif\r\nContextMenuProvider.HOURS                       = {0} Heures\r\nContextMenuProvider.MINUTES                     = {0} Minutes\r\nContextMenuProvider.MINUTES_SHORT               = {0}min\r\nContextMenuProvider.SHOW_DAYS                   = Afficher Jours\r\nContextMenuProvider.SHOW_HOURS                  = Afficher Heures\r\n\r\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED       = Impossible de cr\\u00E9er \\\r\n                                                      une nouvelle \\\r\n                                                      entr\\u00E9e. Calendrier \\\r\n                                                      non defini.\r\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY = Impossible de cr\\u00E9er \\\r\n                                                      une nouvelle \\\r\n                                                      entr\\u00E9e. Les \\\r\n                                                      calendars sont soit en \\\r\n                                                      lecture seul ou \\\r\n                                                      inactifs.{0}{0}Veuiller \\\r\n                                                      vous rassurer qu'au \\\r\n                                                      moins un calendrier est \\\r\n                                                      \\u00E9ditable et actif \\\r\n                                                      and enabled. Puis \\\r\n                                                      essayer encore de double \\\r\n                                                      cliquer.\r\nDateControl.DEFAULT_CALENDAR_NAME                   = Defaut\r\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME            = Defaut\r\nDateControl.DEFAULT_ENTRY_TITLE=Nouvelle entr\\u00E9e\r\nDateControl.DEFAULT_NEW_CALENDAR                    = Calendrier\r\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE             = Source de Calendrier \r\nDateControl.DEFAULT_VIRTUAL_GRID_NAME               = 15 Minutes\r\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME         = 15 Min\r\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED        = Aucun calendrier defini.\r\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY  = Impossible de cr\\u00E9er \\\r\n                                                      une nouvelle entr\\u00E9e.\r\nDateControl.MENU_CALENDAR                           = Calendrier\r\nDateControl.MENU_ITEM_DELETE                        = Supprimer\r\nDateControl.MENU_ITEM_INFORMATION                   = Information\r\nDateControl.NO_CONTENT                              = Pas de contenu\r\nDateControl.TITLE_CALENDAR_PROBLEM                  = Probl\\u00E8me de \\\r\n                                                      Calendrier\r\n\r\nDayPage.DATE_FORMATTER               = EEEE, dd. MMMM yyyy\r\nDayPage.TOOLTIP_LAYOUT               = Basculer la disposition du calendrier\r\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST = Maximiser l'affichage de la liste de \\\r\n                                       l'agenda\r\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW    = Maximiser l'affichage du calendrier \\\r\n                                       journalier\r\nDayPage.TOOLTIP_STANDARD_LAYOUT      = Afficher la liste d'agenda et le \\\r\n                                       calendrier journalier\r\n\r\nDetailedDayViewSkin.ALL_DAY = Toute la Journ\\u00E9e\r\n\r\nDetailedWeekViewSkin.ALL_DAY = Toute la journ\\u00E9e\r\n\r\nEntriesPane.FULL_DAY = Journ\\u00E9e enti\\u00E8re\r\n\r\nEntryDetailsView.CUSTOM                = Personnalis\\u00E9\r\nEntryDetailsView.DAILY                 = Journalier\r\nEntryDetailsView.FROM                  = Du:\r\nEntryDetailsView.FULL_DAY              = Journ\\u00E9e enti\\u00E8re:\r\nEntryDetailsView.MENU_BUTTON_NONE      = Rien\r\nEntryDetailsView.MENU_ITEM_CUSTOM      = Personnalis\\u00E9...\r\nEntryDetailsView.MENU_ITEM_EVERY_DAY   = Tout les Jours\r\nEntryDetailsView.MENU_ITEM_EVERY_MONTH = Tout les Mois\r\nEntryDetailsView.MENU_ITEM_EVERY_WEEK  = Toute les Semaines\r\nEntryDetailsView.MENU_ITEM_EVERY_YEAR  = Chaque Ann\\u00E9e\r\nEntryDetailsView.MENU_ITEM_NONE        = Rien\r\nEntryDetailsView.MONTHLY               = Mensuel\r\nEntryDetailsView.NONE                  = Rien\r\nEntryDetailsView.REPEAT                = R\\u00E9p\\u00E9ter:\r\nEntryDetailsView.TIMEZONE              = Fuseau horaire:\r\nEntryDetailsView.TO                    = Au:\r\nEntryDetailsView.WEEKLY                = Hebdomadaire\r\nEntryDetailsView.YEARLY                = Annuel\r\n\r\nEntryHeaderView.PROMPT_LOCATION = Ajouter une Localit\\u00E9\r\nEntryHeaderView.PROMPT_TITLE    = Titre\r\n\r\nEntryPopOverContentPane.DETAILS = D\\u00E9tails\r\n\r\nMargin.CUSTOM         = Personnalis\\u00E9\r\nMargin.DEFAULT        = D\\u00E9faut\r\nMargin.MINIMUM        = Minimum\r\n\r\nMarginSelector.BOTTOM = Bas\r\nMarginSelector.LEFT   = Gauche\r\nMarginSelector.RIGHT  = Droite\r\nMarginSelector.TOP    = Haut\r\n\r\nMonthEntryViewSkin.ENDS_AT = fini le {0}\r\n\r\nMonthPage.DATE_FORMAT = MMMM yyyy\r\n\r\nMonthSheetView.ADD_NEW_EVENT  = Nouveau \\u00E9v\\u00E8nement\r\nMonthSheetView.BADGE_CELLS    = Compteur\r\nMonthSheetView.DETAIL_CELLS   = D\\u00E9tail\r\nMonthSheetView.STANDARD_CELLS = Standard\r\nMonthSheetView.USAGE_CELLS    = Usage\r\n\r\nMonthViewSkin.MORE_ENTRIES      = {0} plus...\r\nMonthViewSkin.TODAY_DATE_FORMAT = EE d\r\n\r\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL  = Tout les \\u00E9v\\u00E8nements du jour\r\nOptionsViewSkin.CALENDAR_KEYS_LABEL   = L\\u00E9gendes du calendrier\r\nOptionsViewSkin.DETAILS_LABEL         = D\\u00E9tails\r\nOptionsViewSkin.MINI_CALENDAR_LABEL   = Mini Calendrier\r\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL = Disposition des colonnes\r\nOptionsViewSkin.TIMED_EVENTS_LABEL    = Ev\\u00E8nements planifi\\u00E9s\r\n\r\nPageBaseSkin.TODAY = Aujourd'hui\r\n\r\nPaperViewSkin.MARGIN_LABEL    = Marges\r\nPaperViewSkin.PAPER_LABEL     = Papier\r\nPaperViewSkin.VIEW_TYPE_LABEL = Vue\r\n\r\nPreviewPaneSkin.ZOOM_LABEL = Zoom\r\n\r\nPrintView.CANCEL_BUTTON               = Annuler\r\nPrintView.CONTINUE_BUTTON             = Continuer\r\nPrintView.ERROR_NO_PRINTER            = Impossible d'imprimer: aucun service \\\r\n                                        d'impression install\\u00E9 sur le syst\\\r\n                                        \\u00E8me.\r\nPrintView.NO_PRINTERS                 = Imprimante Indisponible\r\nPrintView.TITLE_LABEL                 = Imprimer\r\n\r\nPrintViewType.DAY_PLURAL_CHRONO       = jours\r\nPrintViewType.DAY_SINGULAR_CHRONO     = Jour\r\nPrintViewType.DAY_VIEW                = Jour\r\nPrintViewType.MONTH_PLURAL_CHRONO     = mois\r\nPrintViewType.MONTH_SINGULAR_CHRONO   = mois\r\nPrintViewType.MONTH_VIEW              = Mois\r\nPrintViewType.OPTIONS_TITLE_LABEL     = Options\r\nPrintViewType.PAPER_TITLE_LABEL       = Papier\r\nPrintViewType.SOURCE_VIEW_TITLE_LABEL = Calendriers\r\nPrintViewType.TIME_RANGE_TITLE_LABEL  = Intervalle de temps\r\nPrintViewType.TO_LABEL                = \\u00E0\r\nPrintViewType.WEEK_PLURAL_CHRONO      = semaines\r\nPrintViewType.WEEK_SINGULAR_CHRONO    = semaine\r\nPrintViewType.WEEK_VIEW               = Semaine\r\n\r\nRecurrencePopupSkin.CANCEL = Annuler\r\nRecurrencePopupSkin.OK     = OK\r\n\r\nRecurrenceViewSkin.32              = Semaines\r\nRecurrenceViewSkin.AFTER           = Apr\\u00E8s\r\nRecurrenceViewSkin.DAILY           = Journalier\r\nRecurrenceViewSkin.DAYS            = Jours\r\nRecurrenceViewSkin.DAY_OF_MONTH    = Jour du Mois\r\nRecurrenceViewSkin.DAY_OF_WEEK     = Jour de la Semaine\r\nRecurrenceViewSkin.ENDS            = Prend fin:\r\nRecurrenceViewSkin.FREQUENCY       = Frequence:\r\nRecurrenceViewSkin.MONTHLY         = Mensuel\r\nRecurrenceViewSkin.MONTHS          = Mois\r\nRecurrenceViewSkin.NEVER           = Jamais\r\nRecurrenceViewSkin.OCCURENCES      = occurences\r\nRecurrenceViewSkin.ON              = Le\r\nRecurrenceViewSkin.REPEAT_BY       = R\\u00E9p\\u00E9ter le:\r\nRecurrenceViewSkin.REPEAT_EVERY    = R\\u00E9p\\u00E9ter chaque:\r\nRecurrenceViewSkin.REPEAT_ON       = R\\u00E9p\\u00E9ter le:\r\nRecurrenceViewSkin.SHORT_FRIDAY    = V\r\nRecurrenceViewSkin.SHORT_MONDAY    = L\r\nRecurrenceViewSkin.SHORT_SATURDAY  = S\r\nRecurrenceViewSkin.SHORT_SUNDAY    = D\r\nRecurrenceViewSkin.SHORT_THURSDAY  = J\r\nRecurrenceViewSkin.SHORT_TUESDAY   = M\r\nRecurrenceViewSkin.SHORT_WEDNESDAY = M\r\nRecurrenceViewSkin.STARTS_ON       = D\\u00E9marre le:\r\nRecurrenceViewSkin.SUMMARY         = R\\u00E9sum\\u00E9:\r\nRecurrenceViewSkin.WEEKLY          = Hebdomadaire\r\nRecurrenceViewSkin.YEARLY          = Annuel\r\nRecurrenceViewSkin.YEARS           = Ann\\u00E9es\r\n\r\nSearchResultViewSkin.FROM_UNTIL           = {0} au {1}\r\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE = {0}, {1} au {2}, {3}\r\n\r\nSourceView.DISABLE_ALL = Tout d\\u00E9sactiver\r\nSourceView.ENABLE_ALL  = Tout Activer\r\n\r\nTimeRangeFieldValue.AFTER_LABEL          = Apr\\u00E8s\r\nTimeRangeFieldValue.APRIL_LABEL          = Avril\r\nTimeRangeFieldValue.AUGUST_LABEL         = Ao\\u00FBt\r\nTimeRangeFieldValue.DECEMBER_LABEL       = D\\u00E9cembre\r\nTimeRangeFieldValue.FEBRUARY_LABEL       = F\\u00E9vrier\r\nTimeRangeFieldValue.JANUARY_LABEL        = Janvier\r\nTimeRangeFieldValue.JULY_LABEL           = Juillet\r\nTimeRangeFieldValue.JUNE_LABEL           = Juin\r\nTimeRangeFieldValue.MARCH_LABEL          = Mars\r\nTimeRangeFieldValue.MAY_LABEL            = Mai\r\nTimeRangeFieldValue.NEXT_MONTH_LABEL     = Mois Prochain\r\nTimeRangeFieldValue.NEXT_WEEK_LABEL      = Semaine Prochaine\r\nTimeRangeFieldValue.NOVEMBER_LABEL       = Novembre\r\nTimeRangeFieldValue.OCTOBER_LABEL        = Octobre\r\nTimeRangeFieldValue.ON_DATE_LABEL        = A la date du\r\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL = A la semaine du\r\nTimeRangeFieldValue.SEPTEMBER_LABEL      = Septembre\r\nTimeRangeFieldValue.THIS_MONTH_LABEL     = Ce Mois\r\nTimeRangeFieldValue.THIS_WEEK_LABEL      = Cette Semaine\r\nTimeRangeFieldValue.TODAY_LABEL          = Aujourd'hui\r\nTimeRangeFieldValue.TOMORROW             = Demain\r\n\r\nTimeRangeViewSkin.END_LABEL             = Fin\r\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL   = Il sera imprim\\u00E9 {0} {1}\r\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR = Il sera imprim\\u00E9 {0} {1}\r\nTimeRangeViewSkin.START_LABEL           = D\\u00E9but\r\n\r\nUtil.DAY                  = jour\r\nUtil.DAYS                 = jours\r\nUtil.EVERY_PLURAL         = Chaque {0} {1}\r\nUtil.EVERY_SINGULAR       = Chaque {0}\r\nUtil.FIFTH                = cinqui\\u00E8me\r\nUtil.FIRST                = premier\r\nUtil.FOURTH               = quatri\\u00E8me\r\nUtil.FRIDAY               = Vendredi\r\nUtil.HOUR                 = heure\r\nUtil.HOURS                = heures\r\nUtil.INVALID_RULE         = R\\u00E8gle de recurrence invalide.\r\nUtil.MINUTE               = minute\r\nUtil.MINUTES              = minutes\r\nUtil.MONDAY               = Lundi\r\nUtil.MONTH                = mois\r\nUtil.MONTHS               = mois\r\nUtil.MONTH_AND_DAY_FORMAT = dd MMMM\r\nUtil.ONCE                 = Une fois\r\nUtil.ON_DATE              = \\ le {0}\r\nUtil.ON_MONTH_DAY         = \\ \\u00E0 la date du\r\nUtil.ON_MONTH_WEEKDAY     = \\ \\u00E0 la date du {0} {1}\r\nUtil.ON_WEEKDAY           = \\ le \r\nUtil.SATURDAY             = Sam\\u00E9di\r\nUtil.SECOND               = seconde\r\nUtil.SECONDS              = secondes\r\nUtil.SUNDAY               = Dimanche\r\nUtil.THIRD                = troisi\\u00E8me\r\nUtil.THURSDAY             = Jeudi\r\nUtil.TIMES                = , {0} fois\r\nUtil.TUESDAY              = Mardi\r\nUtil.UNTIL_DATE           = , jusqu'\\u00E0 {0}\r\nUtil.WEDNESDAY            = Mercredi\r\nUtil.WEEK                 = semaine\r\nUtil.WEEKS                = semaines\r\nUtil.YEAR                 = ann\\u00E9e\r\nUtil.YEARS                = ann\\u00E9es\r\n\r\nVirtualGrid.OFF       = D\\u00E9sactiv\\u00E9\r\nVirtualGrid.OFF_SHORT = D\r\n\r\nWeekDayHeaderView.CELL_DATE_FORMAT = EEE, d\r\n\r\nWeekPage.DATE_FORMAT    = MMMM yyyy\r\nWeekPage.TOOLTIP_LAYOUT = Basculer la disposition du calendrier\r\n\r\nYearMonthViewSkin.MONTH_FORMAT = MMMM\r\nYearMonthViewSkin.TODAY        = Aujourd'hui\r\nYearMonthViewSkin.YEAR_FORMAT  = yyyy\r\n\r\nYearPage.DATE_FORMAT          = yyyy\r\nYearPage.TOOLTIP_DISPLAY_MODE = Vue Grille / Colonne\r\n"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_it.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} Giorni\nAgendaView.MENU_ITEM_LOOK_AHEAD=Periodo successivo\nAgendaView.MENU_ITEM_LOOK_BACK=Periodo precedente\n\nContextMenuProvider.ADD_NEW_EVENT=Aggiungi nuovo evento\nContextMenuProvider.DAYS={0} Giorni\nContextMenuProvider.EARLY_LATE_HOURS=Orario notturno\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Comprimi\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Nascondi\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Mostra\nContextMenuProvider.GRID=Griglia\nContextMenuProvider.GRID_OFF=Off\nContextMenuProvider.HOURS={0} Ore\nContextMenuProvider.MINUTES={0} Minuti\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Mostra giorni\nContextMenuProvider.SHOW_HOURS=Mostra ore\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=Impossibile creare una nuova voce. Nessun calendario disponibile.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Impossibile creare una nuova voce. Tutti i calendari sono di sola lettura o non abilitati.{0}{0}Assicurarsi che almeno un calendario sia editabile e abilitato. Poi riprovare con il doppio click.\nDateControl.DEFAULT_CALENDAR_NAME=Predefinito\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Predefinito\nDateControl.DEFAULT_ENTRY_TITLE=Nuova voce\nDateControl.DEFAULT_NEW_CALENDAR=Calendario\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Origine Calendario\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 Minuti\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 Min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=Nessun calendario definito.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Impossibile creare una nuova voce.\nDateControl.MENU_CALENDAR=Calendario\nDateControl.MENU_ITEM_DELETE=Cancella\nDateControl.MENU_ITEM_INFORMATION=Informazioni\nDateControl.NO_CONTENT=Nessun contenuto\nDateControl.TITLE_CALENDAR_PROBLEM=Problema Calendario\n\nVirtualGrid.OFF=Off\nVirtualGrid.OFF_SHORT=Off\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=giorno\nUtil.DAYS=giorni\nUtil.EVERY_PLURAL=Ogni {0} {1}\nUtil.EVERY_SINGULAR=Ogni {0}\nUtil.FIFTH=quinto\nUtil.FIRST=primo\nUtil.FOURTH=quarto\nUtil.FRIDAY=Venerd\\u00C3\\u00AC\nUtil.HOUR=ora\nUtil.HOURS=ore\nUtil.INVALID_RULE=Regola di ricorrenza non valida.\nUtil.MINUTE=minuto\nUtil.MINUTES=minuti\nUtil.MONDAY=Luned\\u00C3\\u00AC\nUtil.MONTH=mese\nUtil.MONTH_AND_DAY_FORMAT=dd MMMM\nUtil.MONTHS=mesi\nUtil.ON_DATE=\\ il {0}\nUtil.ON_MONTH_DAY=\\ il giorno \nUtil.ON_MONTH_WEEKDAY=\\ il {0} di {1}\nUtil.ON_WEEKDAY=\\ il \nUtil.ONCE=Una volta\nUtil.SATURDAY=Sabato\nUtil.SECOND=secondo\nUtil.SECONDS=secondi\nUtil.SUNDAY=Domenica\nUtil.THIRD=terzo\nUtil.THURSDAY=Gioved\\u00C3\\u00AC\nUtil.TIMES=, {0} volte\nUtil.TUESDAY=Marted\\u00C3\\u00AC\nUtil.UNTIL_DATE=, fino a {0}\nUtil.WEDNESDAY=Mercoled\\u00C3\\u00AC\nUtil.WEEK=settimana\nUtil.WEEKS=settimane\nUtil.YEAR=anno\nUtil.YEARS=annI\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM yyyy\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Massimizza la lista agenda\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Massimizza la programmazione del giorno\nDayPage.TOOLTIP_STANDARD_LAYOUT=Mostra la lista agenda e la programmazione del giorno\nDayPage.TOOLTIP_LAYOUT=Cambia visualizzazione calendario\n\nMonthPage.DATE_FORMAT=MMMM yyyy\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Cambia visualizzazione calendario\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Vista griglia / Colonna\n\nEntriesPane.FULL_DAY=Giorno intero\nEntryDetailsView.CUSTOM=Personalizzato\nEntryDetailsView.DAILY=Giornalmente\nEntryDetailsView.FROM=Da:\nEntryDetailsView.FULL_DAY=Giorno intero:\nEntryDetailsView.MENU_BUTTON_NONE=Nessuno\nEntryDetailsView.MENU_ITEM_CUSTOM=Peronalizza...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Ogni giorno\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Ogni mese\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Ogni settimana\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Ogni anno\nEntryDetailsView.MENU_ITEM_NONE=Nessuno\nEntryDetailsView.MONTHLY=Mensilmente\nEntryDetailsView.NONE=Nessuno\nEntryDetailsView.REPEAT=Ripetizione:\nEntryDetailsView.TIMEZONE=Fuso orario:\nEntryDetailsView.TO=A:\nEntryDetailsView.WEEKLY=Settimanalmente\nEntryDetailsView.YEARLY=Annualmente\nEntryHeaderView.PROMPT_LOCATION=Aggiungi luogo\nEntryHeaderView.PROMPT_TITLE=Titolo\n\nEntryPopOverContentPane.DETAILS=Dettagli\n\nRecurrencePopupSkin.CANCEL=Cancella\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=Tutto il giorno\nPageBaseSkin.TODAY=Oggi\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE=da {0} a {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, da {1} a {2}, {3}\nAgendaEntryCell.ALL_DAY=Tutto il giorno\n\nAgendaViewSkin.NO_ENTRIES=Nessuna voce\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda da {0} fino {1}\n\nMonthEntryViewSkin.ENDS_AT=finisce il {0}\nMonthViewSkin.MORE_ENTRIES={0} altro...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=Settimane\nRecurrenceViewSkin.AFTER=Dopo\nRecurrenceViewSkin.DAILY=Giornalmente\nRecurrenceViewSkin.DAY_OF_MONTH=Giorno del Mese\nRecurrenceViewSkin.DAY_OF_WEEK=Giorno della Settimana\nRecurrenceViewSkin.DAYS=Giorno\nRecurrenceViewSkin.ENDS=Fino a:\nRecurrenceViewSkin.FREQUENCY=Frequenza:\nRecurrenceViewSkin.MONTHLY=Mensilmente\nRecurrenceViewSkin.MONTHS=Mesi\nRecurrenceViewSkin.NEVER=Mai\nRecurrenceViewSkin.OCCURENCES=occorrenze\nRecurrenceViewSkin.ON=On\nRecurrenceViewSkin.REPEAT_BY=Ripeti da:\nRecurrenceViewSkin.REPEAT_EVERY=Ripeti ogni:\nRecurrenceViewSkin.REPEAT_ON=Ripeti il:\nRecurrenceViewSkin.SHORT_FRIDAY=V\nRecurrenceViewSkin.SHORT_MONDAY=L\nRecurrenceViewSkin.SHORT_SATURDAY=S\nRecurrenceViewSkin.SHORT_SUNDAY=S\nRecurrenceViewSkin.SHORT_THURSDAY=G\nRecurrenceViewSkin.SHORT_TUESDAY=M\nRecurrenceViewSkin.SHORT_WEDNESDAY=M\nRecurrenceViewSkin.STARTS_ON=Inizia il:\nRecurrenceViewSkin.SUMMARY=Sommario:\nRecurrenceViewSkin.WEEKLY=Settimanalmente\nRecurrenceViewSkin.YEARLY=Annualmente\nRecurrenceViewSkin.YEARS=Anni\n\nSearchResultViewSkin.FROM_UNTIL=da {0} a {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, da {1} a {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=Tutto il giorno\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Oggi\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Calendari\nCalendarViewSkin.TOGGLE_SHOW_DAY=Giorno\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Mostra una settimana\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Mostra un mese\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Mostra un anno\nCalendarViewSkin.PROMPT_SEARCH_FIELD=Ricerca\nCalendarViewSkin.TOGGLE_SHOW_WEEK=Settimana\nCalendarViewSkin.TOGGLE_SHOW_MONTH=Mese\nCalendarViewSkin.TOGGLE_SHOW_YEAR=Anno\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Mostra tutti i calendari\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Aggiungi un calendario\nCalendarViewSkin.TOOLTIP_PRINT=Stampa\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Mostra un giorno\n\nPrintViewType.DAY_VIEW=Giorno\nPrintViewType.DAY_SINGULAR_CHRONO=giorno\nPrintViewType.DAY_PLURAL_CHRONO=giorni\nPrintViewType.WEEK_VIEW=Settimana\nPrintViewType.WEEK_SINGULAR_CHRONO=settimana\nPrintViewType.WEEK_PLURAL_CHRONO=settimane\nPrintViewType.MONTH_VIEW=Mese\nPrintViewType.MONTH_SINGULAR_CHRONO=mese\nPrintViewType.MONTH_PLURAL_CHRONO=mesi\nPrintViewType.TO_LABEL=a\nPrintViewType.PAPER_TITLE_LABEL=Carta\nPrintViewType.TIME_RANGE_TITLE_LABEL=Intervallo orari\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Calendari\nPrintViewType.OPTIONS_TITLE_LABEL=Opzioni\n\nPaperViewSkin.VIEW_TYPE_LABEL=Vista\nPaperViewSkin.PAPER_LABEL=Carta\nPaperViewSkin.MARGIN_LABEL=Margine\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=Voci tutta la giornata\nOptionsViewSkin.DETAILS_LABEL=Dettagli\nOptionsViewSkin.TIMED_EVENTS_LABEL=Voci a tempo\nOptionsViewSkin.MINI_CALENDAR_LABEL=Mini Calendario\nOptionsViewSkin.CALENDAR_KEYS_LABEL=Chiavi Calendario\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Visualizzazione colonne\n\nTimeRangeViewSkin.START_LABEL=Inizio\nTimeRangeViewSkin.END_LABEL=fine\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR={0} {1} verr\\u00C3\\u00A0 stampato\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL={0} {1} verranno stampati\n\nTimeRangeFieldValue.TODAY_LABEL=Oggi\nTimeRangeFieldValue.TOMORROW=Domani\nTimeRangeFieldValue.ON_DATE_LABEL=La data\nTimeRangeFieldValue.THIS_WEEK_LABEL=Questa settimana\nTimeRangeFieldValue.NEXT_WEEK_LABEL=Prossima settimana\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=La settimana\nTimeRangeFieldValue.THIS_MONTH_LABEL=Questo mese\nTimeRangeFieldValue.NEXT_MONTH_LABEL=Prossimo mese\nTimeRangeFieldValue.JANUARY_LABEL=Gennaio\nTimeRangeFieldValue.FEBRUARY_LABEL=Febbraio\nTimeRangeFieldValue.MARCH_LABEL=Marzo\nTimeRangeFieldValue.APRIL_LABEL=Aprile\nTimeRangeFieldValue.MAY_LABEL=Maggio\nTimeRangeFieldValue.JUNE_LABEL=Giugno\nTimeRangeFieldValue.JULY_LABEL=Luglio\nTimeRangeFieldValue.AUGUST_LABEL=Agosto\nTimeRangeFieldValue.SEPTEMBER_LABEL=Settembre\nTimeRangeFieldValue.OCTOBER_LABEL=Ottobre\nTimeRangeFieldValue.NOVEMBER_LABEL=Novembre\nTimeRangeFieldValue.DECEMBER_LABEL=Dicembre\nTimeRangeFieldValue.AFTER_LABEL=Dopo\n\nPreviewPaneSkin.ZOOM_LABEL=Zoom\n\nPrintView.TITLE_LABEL=Stampa\nPrintView.CONTINUE_BUTTON=Continua\nPrintView.CANCEL_BUTTON=Cancella\nPrintView.NO_PRINTERS = Nessuna stampante disponibile.\nPrintView.ERROR_NO_PRINTER = Impossibile stampare, non ci sono servizi di stampa installati sul sistema.\n\nMargin.DEFAULT=Default\nMargin.MINIMUM=Minimo\nMargin.CUSTOM=Personalizzato\n\nMarginSelector.TOP=Alto\nMarginSelector.RIGHT=Destra\nMarginSelector.BOTTOM=Basso\nMarginSelector.LEFT=Sinistra\n\nSourceView.DISABLE_ALL=Disabilita tutti\nSourceView.ENABLE_ALL=Abilita tutti\n\nMonthSheetView.ADD_NEW_EVENT=Aggiungi nuovo evento\nMonthSheetView.STANDARD_CELLS=Standard\nMonthSheetView.USAGE_CELLS=Utilizzo\nMonthSheetView.DETAIL_CELLS=Dettaglio\nMonthSheetView.BADGE_CELLS=Contatore\n"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_pt_BR.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} Dias\nAgendaView.MENU_ITEM_LOOK_AHEAD=Per\\u00edodo \\u00e0 Frente\nAgendaView.MENU_ITEM_LOOK_BACK=Per\\u00edodo Atr\\u00e1s\n\nContextMenuProvider.ADD_NEW_EVENT=Adicionar Novo Evento\nContextMenuProvider.DAYS={0} Dias\nContextMenuProvider.EARLY_LATE_HOURS=Hor\\u00e1rio matinal / vespertino\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Comprimir\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Ocultar\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Mostrar\nContextMenuProvider.GRID=Grade\nContextMenuProvider.GRID_OFF=Desativada\nContextMenuProvider.HOURS={0} Horas\nContextMenuProvider.MINUTES={0} Minutos\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Mostrar Dias\nContextMenuProvider.SHOW_HOURS=Mostrar Horas\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=Imposs\\u00edvel criar uma nova entrada. Nenhum calend\\u00e1rio foi definido.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Imposs\\u00edvel criar uma nova entrada. Todos os calend\\u00e1rios est\\u00e3o ou somente leitura ou n\\u00e3o est\\u00e3o habilitados.{0}{0}Por favor certifique-se que pelo menos um calend\\u00e1rio pode ser escrito e est\\u00e1 habilitado. Ent\\u00e3o tente novamente o clique duplo.\nDateControl.DEFAULT_CALENDAR_NAME=Padr\\u00e3o\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Padr\\u00e3o\nDateControl.DEFAULT_ENTRY_TITLE=Nova Entrada {0}\nDateControl.DEFAULT_NEW_CALENDAR=Calend\\u00e1rio\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Calendar Source\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 Minutos\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 Min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=Nenhum calend\\u00e1rio definido.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Imposs\\u00edvel criar um novo registro.\nDateControl.MENU_CALENDAR=Calend\\u00e1rio\nDateControl.MENU_ITEM_DELETE=Excluir\nDateControl.MENU_ITEM_INFORMATION=Informa\\u00e7\\u00e3o\nDateControl.NO_CONTENT=Sem Conte\\u00fado\nDateControl.TITLE_CALENDAR_PROBLEM=Problema no Calend\\u00e1rio\n\nVirtualGrid.OFF=Off\nVirtualGrid.OFF_SHORT=Off\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=dia\nUtil.DAYS=dias\nUtil.EVERY_PLURAL=Cada {0} {1}\nUtil.EVERY_SINGULAR=Cada {0}\nUtil.FIFTH=quinto\nUtil.FIRST=primeiro\nUtil.FOURTH=quarto\nUtil.FRIDAY=Sexta\nUtil.HOUR=hora\nUtil.HOURS=horas\nUtil.INVALID_RULE=Regra de recorr\\u00eancia inv\\u00e1lida.\nUtil.MINUTE=minuto\nUtil.MINUTES=minutos\nUtil.MONDAY=Segunda\nUtil.MONTH=m\\u00eas\nUtil.MONTH_AND_DAY_FORMAT=MMMM dd\nUtil.MONTHS=meses\nUtil.ON_DATE=\\\\ em {0}\nUtil.ON_MONTH_DAY=\\\\ no dia \nUtil.ON_MONTH_WEEKDAY=\\\\ no {0} {1}\nUtil.ON_WEEKDAY=\\\\ em \nUtil.ONCE=Uma vez\nUtil.SATURDAY=S\\u00E1bado\nUtil.SECOND=segundo\nUtil.SECONDS=segundos\nUtil.SUNDAY=Domingo\nUtil.THIRD=terceiro\nUtil.THURSDAY=Quinta\nUtil.TIMES=, {0} vezes\nUtil.TUESDAY=Ter\\u00e7a\nUtil.UNTIL_DATE=, at\\u00e9 {0}\nUtil.WEDNESDAY=Quarta\nUtil.WEEK=semana\nUtil.WEEKS=semanas\nUtil.YEAR=ano\nUtil.YEARS=anos\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM yyyy\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Maximiza o display da lista da agenda\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Maximiza o display do cronograma do dia\nDayPage.TOOLTIP_STANDARD_LAYOUT=Exibe a lista da agenda e o cronograma do dia\nDayPage.TOOLTIP_LAYOUT=Alterna layout do calend\\u00e1rio\n\nMonthPage.DATE_FORMAT=MMMM yyyy\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Alterna layout do calend\\u00e1rio\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Exibi\\u00e7\\u00e3o Grade / Coluna\n\nEntriesPane.FULL_DAY=Dia Inteiro\nEntryDetailsView.CUSTOM=Personalizado\nEntryDetailsView.DAILY=Diariamente\nEntryDetailsView.FROM=De:\nEntryDetailsView.FULL_DAY=Dia Inteiro:\nEntryDetailsView.MENU_BUTTON_NONE=Nenhum\nEntryDetailsView.MENU_ITEM_CUSTOM=Personalizado...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Todo Dia\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Todo M\\u00eas\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Toda Semana\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Todo Ano\nEntryDetailsView.MENU_ITEM_NONE=Nenhum\nEntryDetailsView.MONTHLY=Mensalmente\nEntryDetailsView.NONE=Nenhum\nEntryDetailsView.REPEAT=Repetir:\nEntryDetailsView.TIMEZONE=Fuso hor\\u00e1rio:\nEntryDetailsView.TO=At\\u00e9:\nEntryDetailsView.WEEKLY=Semanalmente\nEntryDetailsView.YEARLY=Anualmente\nEntryHeaderView.PROMPT_LOCATION=Adicionar local\nEntryHeaderView.PROMPT_TITLE=T\\u00edtulo\n\nEntryPopOverContentPane.DETAILS=Detalhes\n\nRecurrencePopupSkin.CANCEL=Cancelar\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=Todo Dia\nPageBaseSkin.TODAY=Hoje\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE={0} at\\u00e9 {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, {1} at\\u00e9 {2}, {3}\nAgendaEntryCell.ALL_DAY=dia-todo\n\nAgendaViewSkin.NO_ENTRIES=Sem Entradas\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda de {0} at\\u00e9 {1}\n\nMonthEntryViewSkin.ENDS_AT=termina em {0}\nMonthViewSkin.MORE_ENTRIES={0} mais...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=Semanas\nRecurrenceViewSkin.AFTER=Depois\nRecurrenceViewSkin.DAILY=Diariamente\nRecurrenceViewSkin.DAY_OF_MONTH=Dia do M\\u00eas\nRecurrenceViewSkin.DAY_OF_WEEK=Dia da Semana\nRecurrenceViewSkin.DAYS=Dias\nRecurrenceViewSkin.ENDS=Termina:\nRecurrenceViewSkin.FREQUENCY=Frequ\\u00eancia:\nRecurrenceViewSkin.MONTHLY=Mensalmente\nRecurrenceViewSkin.MONTHS=Meses\nRecurrenceViewSkin.NEVER=Nunca\nRecurrenceViewSkin.OCCURENCES=ocorr\\u00eancias\nRecurrenceViewSkin.ON=Em\nRecurrenceViewSkin.REPEAT_BY=Repetir por:\nRecurrenceViewSkin.REPEAT_EVERY=Repetir a cada:\nRecurrenceViewSkin.REPEAT_ON=Repetir em:\nRecurrenceViewSkin.SHORT_FRIDAY=S\nRecurrenceViewSkin.SHORT_MONDAY=S\nRecurrenceViewSkin.SHORT_SATURDAY=S\nRecurrenceViewSkin.SHORT_SUNDAY=D\nRecurrenceViewSkin.SHORT_THURSDAY=Q\nRecurrenceViewSkin.SHORT_TUESDAY=T\nRecurrenceViewSkin.SHORT_WEDNESDAY=Q\nRecurrenceViewSkin.STARTS_ON=Come\\u00e7a em:\nRecurrenceViewSkin.SUMMARY=Resumo:\nRecurrenceViewSkin.WEEKLY=Semanalmente\nRecurrenceViewSkin.YEARLY=Anualmente\nRecurrenceViewSkin.YEARS=Anos\n\nSearchResultViewSkin.FROM_UNTIL={0} at\\u00e9 {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, {1} at\\u00e9 {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=Dia Todo\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Hoje\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Calend\\u00e1rios\nCalendarViewSkin.TOGGLE_SHOW_DAY=Dia\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Mostrar uma semana\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Mostrar um m\\u00eas\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Mostrar um ano\nCalendarViewSkin.PROMPT_SEARCH_FIELD=Procurar\nCalendarViewSkin.TOGGLE_SHOW_WEEK=Semana\nCalendarViewSkin.TOGGLE_SHOW_MONTH=M\\u00eas\nCalendarViewSkin.TOGGLE_SHOW_YEAR=Ano\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Mostrar lista de todos os calend\\u00e1rios\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Adicionar um calend\\u00e1rio\nCalendarViewSkin.TOOLTIP_PRINT=Imprimir\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Mostrar um dia\n\nPrintViewType.DAY_VIEW=Dia\nPrintViewType.DAY_SINGULAR_CHRONO=dia\nPrintViewType.DAY_PLURAL_CHRONO=dias\nPrintViewType.WEEK_VIEW=Semana\nPrintViewType.WEEK_SINGULAR_CHRONO=semana\nPrintViewType.WEEK_PLURAL_CHRONO=semanas\nPrintViewType.MONTH_VIEW=M\\u00EAs\nPrintViewType.MONTH_SINGULAR_CHRONO=m\\u00eas\nPrintViewType.MONTH_PLURAL_CHRONO=m\\u00eases\nPrintViewType.TO_LABEL=at\\u00e9\nPrintViewType.PAPER_TITLE_LABEL=Papel\nPrintViewType.TIME_RANGE_TITLE_LABEL=Per\\u00edodo de tempo\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Calend\\u00e1rios\nPrintViewType.OPTIONS_TITLE_LABEL=Op\\u00e7\\u00f5es\n\nPaperViewSkin.VIEW_TYPE_LABEL=Visualiza\\u00e7\\u00e3o\nPaperViewSkin.PAPER_LABEL=Papel\nPaperViewSkin.MARGIN_LABEL=Margem\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=Entradas de Dia Todo\nOptionsViewSkin.DETAILS_LABEL=Detalhes\nOptionsViewSkin.TIMED_EVENTS_LABEL=entradas Programadas\nOptionsViewSkin.MINI_CALENDAR_LABEL=Mini Calend\\u00e1rio\nOptionsViewSkin.CALENDAR_KEYS_LABEL=Chaves Calend\\u00e1rio\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Layout Coluna\n\nTimeRangeViewSkin.START_LABEL=In\\u00edcio\nTimeRangeViewSkin.END_LABEL=Fim\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR={0} {1} vai ser impresso\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL={0} {1} ir\\u00e3o ser impressos\n\nTimeRangeFieldValue.TODAY_LABEL=Hoje\nTimeRangeFieldValue.TOMORROW=Amanh\\u00e3\nTimeRangeFieldValue.ON_DATE_LABEL=Na Data\nTimeRangeFieldValue.THIS_WEEK_LABEL=Essa Semana\nTimeRangeFieldValue.NEXT_WEEK_LABEL=Pr\\u00f3xima Semana\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=Na Semana\nTimeRangeFieldValue.THIS_MONTH_LABEL=Esse M\\u00eas\nTimeRangeFieldValue.NEXT_MONTH_LABEL=Pr\\u00f3ximo M\\u00eas\nTimeRangeFieldValue.JANUARY_LABEL=Janeiro\nTimeRangeFieldValue.FEBRUARY_LABEL=Fevereiro\nTimeRangeFieldValue.MARCH_LABEL=Mar\\u00e7o\nTimeRangeFieldValue.APRIL_LABEL=Abril\nTimeRangeFieldValue.MAY_LABEL=Maio\nTimeRangeFieldValue.JUNE_LABEL=Junho\nTimeRangeFieldValue.JULY_LABEL=Julho\nTimeRangeFieldValue.AUGUST_LABEL=Agosto\nTimeRangeFieldValue.SEPTEMBER_LABEL=Setembro\nTimeRangeFieldValue.OCTOBER_LABEL=Outubro\nTimeRangeFieldValue.NOVEMBER_LABEL=Novembro\nTimeRangeFieldValue.DECEMBER_LABEL=Dezembro\nTimeRangeFieldValue.AFTER_LABEL=Depois\n\nPreviewPaneSkin.ZOOM_LABEL=Zoom\n\nPrintView.TITLE_LABEL=Imprimir\nPrintView.CONTINUE_BUTTON=Continuar\nPrintView.CANCEL_BUTTON=Cancelar\nPrintView.NO_PRINTERS = Nenhuma impressora dispon\\u00edvel.\nPrintView.ERROR_NO_PRINTER = N\\u00e3o \\u00e9 poss\\u00edvel imprimir pois n\\u00e3o h\\u00e1 nenhum servi\\u00e7o de impress\\u00e3o instalado nesse sistema.\n\nMargin.DEFAULT=Padr\\u00e3o\nMargin.MINIMUM=M\\u00ednimo\nMargin.CUSTOM=Customizada\n\nMarginSelector.TOP=Topo\nMarginSelector.RIGHT=Direita\nMarginSelector.BOTTOM=Inferior\nMarginSelector.LEFT=Esquerda\n\nSourceView.DISABLE_ALL=Desabilitar todos\nSourceView.ENABLE_ALL=Habilitar todos\n\nMonthSheetView.ADD_NEW_EVENT=Adicionar Novo Evento\nMonthSheetView.STANDARD_CELLS=Padr\\u00e3o\nMonthSheetView.USAGE_CELLS=Uso\nMonthSheetView.DETAIL_CELLS=Detalhe\nMonthSheetView.BADGE_CELLS=Contador\n"
  },
  {
    "path": "CalendarFXView/src/main/resources/com/calendarfx/view/messages_sk.properties",
    "content": "AgendaView.MENU_ITEM_DAYS={0} dn\\u00ED\nAgendaView.MENU_ITEM_LOOK_AHEAD=Obdobie n\\u00E1h\\u013Eadu\nAgendaView.MENU_ITEM_LOOK_BACK=Obdobie sp\\u00E4tn\\u00E9ho n\\u00E1h\\u013Eadu\n\nContextMenuProvider.ADD_NEW_EVENT=Prida\\u0165 nov\\u00FA udalos\\u0165\nContextMenuProvider.DAYS={0} dn\\u00ED\nContextMenuProvider.EARLY_LATE_HOURS=Skor\\u00E9/neskor\\u00E9 hodiny\nContextMenuProvider.EARLY_LATE_HOURS_COMPRESSED=Komprimova\\u0165\nContextMenuProvider.EARLY_LATE_HOURS_HIDE=Skry\\u0165\nContextMenuProvider.EARLY_LATE_HOURS_SHOW=Zobrazi\\u0165\nContextMenuProvider.GRID=Mrie\\u017Eka\nContextMenuProvider.GRID_OFF=Vyp.\nContextMenuProvider.HOURS={0} hod\\u00EDn\nContextMenuProvider.MINUTES={0} min\\u00FAt\nContextMenuProvider.MINUTES_SHORT={0}min\nContextMenuProvider.SHOW_DAYS=Zobrazi\\u0165 dni\nContextMenuProvider.SHOW_HOURS=Zobrazi\\u0165 hodiny\n\nDateControl.CONTENT_TEXT_NO_CALENDARS_DEFINED=Ned\\u00E1 sa vytvori\\u0165 nov\\u00FD z\\u00E1znam. Moment\\u00E1lne nie s\\u00FA definovan\\u00E9 \\u017Eiadne kalend\\u00E1re.\nDateControl.CONTENT_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Ned\\u00E1 sa vytvori\\u0165 nov\\u00FD z\\u00E1znam. V\\u0161etky kalend\\u00E1re s\\u00FA bu\\u010F iba na \\u010D\\u00EDtanie, alebo moment\\u00E1lne nie s\\u00FA povolen\\u00E9.{0}{0}Uistite sa, \\u017Ee aspo\\u0148 do jedn\\u00E9ho kalend\\u00E1ra je mo\\u017En\\u00E9 zapisova\\u0165 a je povolen\\u00FD. Potom sk\\u00FAste znova dvakr\\u00E1t klikn\\u00FA\\u0165.\nDateControl.DEFAULT_CALENDAR_NAME=Predvolen\\u00E9\nDateControl.DEFAULT_CALENDAR_SOURCE_NAME=Predvolen\\u00E9\nDateControl.DEFAULT_ENTRY_TITLE=Nov\\u00E1 polo\\u017Eka {0}\nDateControl.DEFAULT_NEW_CALENDAR=Kalend\\u00E1r\nDateControl.DEFAULT_NEW_CALENDAR_SOURCE=Zdroj kalend\\u00E1ra\nDateControl.DEFAULT_VIRTUAL_GRID_NAME=15 min\\u00FAt\nDateControl.DEFAULT_VIRTUAL_GRID_SHORT_NAME=15 min\nDateControl.HEADER_TEXT_NO_CALENDARS_DEFINED=Nie s\\u00FA definovan\\u00E9 \\u017Eiadne kalend\\u00E1re.\nDateControl.HEADER_TEXT_UNABLE_TO_CREATE_NEW_ENTRY=Ned\\u00E1 sa vytvori\\u0165 nov\\u00FD z\\u00E1znam.\nDateControl.MENU_CALENDAR=Kalend\\u00E1r\nDateControl.MENU_ITEM_DELETE=Odstr\\u00E1ni\\u0165\nDateControl.MENU_ITEM_INFORMATION=Inform\\u00E1cie\nDateControl.NO_CONTENT=\\u017Eiadny obsah\nDateControl.TITLE_CALENDAR_PROBLEM=Probl\\u00E9m s kalend\\u00E1rom\n\nVirtualGrid.OFF=Vyp\nVirtualGrid.OFF_SHORT=Vyp\n\nWeekDayHeaderView.CELL_DATE_FORMAT=EEE, d\n\nUtil.DAY=de\\u0148\nUtil.DAYS=dni\nUtil.EVERY_PLURAL=Ka\\u017Ed\\u00FDch {0} {1}\nUtil.EVERY_SINGULAR=Ka\\u017Ed\\u00FDch {0}\nUtil.FIFTH=piaty\nUtil.FIRST=prv\\u00FD\nUtil.FOURTH=\\u0161tvrt\\u00FD\nUtil.FRIDAY=piatok\nUtil.HOUR=hodina\nUtil.HOURS=hodiny\nUtil.INVALID_RULE=Neplatn\\u00E9 pravidlo opakovania.\nUtil.MINUTE=min\\u00FAta\nUtil.MINUTES=min\\u00FAt\nUtil.MONDAY=pondelok\nUtil.MONTH=mesiac\nUtil.MONTH_AND_DAY_FORMAT=MMMM dd\nUtil.MONTHS=mesiace\nUtil.ON_DATE=\\ d\\u0148a {0}\nUtil.ON_MONTH_DAY=\\ v de\\u0148\nUtil.ON_MONTH_WEEKDAY=\\ d\\u0148a {0} {1}\nUtil.ON_WEEKDAY=\\ zapnut\\u00E9\nUtil.ONCE=Raz\nUtil.SATURDAY=sobota\nUtil.SECOND=sekunda\nUtil.SECONDS=sek\\u00FAnd\nUtil.SUNDAY=nede\\u013Ea\nUtil.THIRD=tretia\nUtil.THURSDAY=\\u0161tvrtok\nUtil.TIMES=, {0}-kr\\u00E1t\nUtil.TUESDAY=utorok\nUtil.UNTIL_DATE=, do {0}\nUtil.WEDNESDAY=streda\nUtil.WEEK=t\\u00FD\\u017Ede\\u0148\nUtil.WEEKS=t\\u00FD\\u017Edne\nUtil.YEAR=rok\nUtil.YEARS=rokov\n\nDayPage.DATE_FORMATTER=EEEE, dd. MMMM rrrr\nDayPage.TOOLTIP_MAXIMIZE_AGENDA_LIST=Maximalizova\\u0165 zobrazenie zoznamu agendy\nDayPage.TOOLTIP_MAXIMIZE_DAY_VIEW=Maximalizova\\u0165 zobrazenia denn\\u00E9ho pl\\u00E1nu\nDayPage.TOOLTIP_STANDARD_LAYOUT=Zobrazi\\u0165 zoznam agendy a denn\\u00FD rozvrh\nDayPage.TOOLTIP_LAYOUT=Prepn\\u00FA\\u0165 rozlo\\u017Eenie kalend\\u00E1ra\n\nMonthPage.DATE_FORMAT=MMMM rrrr\n\nWeekPage.DATE_FORMAT=MMMM yyyy\nWeekPage.TOOLTIP_LAYOUT=Prepn\\u00FA\\u0165 rozlo\\u017Eenie kalend\\u00E1ra\n\nYearPage.DATE_FORMAT=yyyy\nYearPage.TOOLTIP_DISPLAY_MODE=Zobrazenie mrie\\u017Eka / st\\u013Apec\n\nEntriesPane.FULL_DAY=Cel\\u00FD de\\u0148\nEntryDetailsView.CUSTOM=Vlastn\\u00E9\nEntryDetailsView.DAILY=Denne\nEntryDetailsView.FROM=Od:\nEntryDetailsView.FULL_DAY=Cel\\u00FD de\\u0148:\nEntryDetailsView.MENU_BUTTON_NONE=\\u017Eiadne\nEntryDetailsView.MENU_ITEM_CUSTOM=Vlastn\\u00E9...\nEntryDetailsView.MENU_ITEM_EVERY_DAY=Ka\\u017Ed\\u00FD de\\u0148\nEntryDetailsView.MENU_ITEM_EVERY_MONTH=Ka\\u017Ed\\u00FD mesiac\nEntryDetailsView.MENU_ITEM_EVERY_WEEK=Ka\\u017Ed\\u00FD t\\u00FD\\u017Ede\\u0148\nEntryDetailsView.MENU_ITEM_EVERY_YEAR=Ka\\u017Ed\\u00FD rok\nEntryDetailsView.MENU_ITEM_NONE=\\u017Eiadne\nEntryDetailsView.MONTHLY=Mesa\\u010Dne\nEntryDetailsView.NONE=\\u017Eiadne\nEntryDetailsView.REPEAT=Opakova\\u0165:\nEntryDetailsView.TIMEZONE=\\u010Casov\\u00E9 p\\u00E1smo:\nEntryDetailsView.TO=Komu:\nEntryDetailsView.WEEKLY=T\\u00FD\\u017Edenne\nEntryDetailsView.YEARLY=Ro\\u010Dne\nEntryHeaderView.PROMPT_LOCATION=Prida\\u0165 umiestnenie\nEntryHeaderView.PROMPT_TITLE=N\\u00E1zov\n\nEntryPopOverContentPane.DETAILS=Podrobnosti\n\nRecurrencePopupSkin.CANCEL=Zru\\u0161i\\u0165\nRecurrencePopupSkin.OK=OK\n\nDetailedDayViewSkin.ALL_DAY=Cel\\u00FD de\\u0148\nPageBaseSkin.TODAY=Dnes\n\nAgendaEntryCell.WEEKDAY_FORMAT=EEEE\nAgendaEntryCell.ENTRY_TIME_RANGE={0} a\\u017E {1}\nAgendaEntryCell.ENTRY_TIME_RANGE_WITH_DATE={0}, {1} a\\u017E {2}, {3}\nAgendaEntryCell.ALL_DAY=cel\\u00FD de\\u0148\n\nAgendaViewSkin.NO_ENTRIES=\\u017Eiadne z\\u00E1znamy\nAgendaViewSkin.AGENDA_TIME_RANGE=Agenda na {0} do {1}\n\nMonthEntryViewSkin.ENDS_AT=kon\\u010D\\u00ED o {0}\nMonthViewSkin.MORE_ENTRIES={0} \\u010Fal\\u0161\\u00EDch...\nMonthViewSkin.TODAY_DATE_FORMAT=EE d\n\nRecurrenceViewSkin.32=T\\u00FD\\u017Edne\nRecurrenceViewSkin.AFTER=Po\nRecurrenceViewSkin.DAILY=Denne\nRecurrenceViewSkin.DAY_OF_MONTH=De\\u0148 v mesiaci\nRecurrenceViewSkin.DAY_OF_WEEK=De\\u0148 v t\\u00FD\\u017Edni\nRecurrenceViewSkin.DAYS=dni\nRecurrenceViewSkin.ENDS=Kon\\u010D\\u00ED:\nRecurrenceViewSkin.FREQUENCY=Frekvencia:\nRecurrenceViewSkin.MONTHLY=Mesa\\u010Dne\nRecurrenceViewSkin.MONTHS=Mesiace\nRecurrenceViewSkin.NEVER=Nikdy\nRecurrenceViewSkin.OCCURENCES=v\\u00FDskyty\nRecurrenceViewSkin.ON=Zapnut\\u00E9\nRecurrenceViewSkin.REPEAT_BY=Opakova\\u0165 pod\\u013Ea:\nRecurrenceViewSkin.REPEAT_EVERY=Opakova\\u0165 ka\\u017Ed\\u00FD:\nRecurrenceViewSkin.REPEAT_ON=Opakova\\u0165 v:\nRecurrenceViewSkin.SHORT_FRIDAY=Pi\nRecurrenceViewSkin.SHORT_MONDAY=Po\nRecurrenceViewSkin.SHORT_SATURDAY=So\nRecurrenceViewSkin.SHORT_SUNDAY=Ne\nRecurrenceViewSkin.SHORT_THURSDAY=\\u0161t\nRecurrenceViewSkin.SHORT_TUESDAY=Ut\nRecurrenceViewSkin.SHORT_WEDNESDAY=St\nRecurrenceViewSkin.STARTS_ON=Za\\u010D\\u00EDna:\nRecurrenceViewSkin.SUMMARY=S\\u00FAhrn:\nRecurrenceViewSkin.WEEKLY=T\\u00FD\\u017Edenne\nRecurrenceViewSkin.YEARLY=Ro\\u010Dne\nRecurrenceViewSkin.YEARS=Roky\n\nSearchResultViewSkin.FROM_UNTIL={0} a\\u017E {1}\nSearchResultViewSkin.FROM_UNTIL_WITH_DATE={0}, {1} a\\u017E {2}, {3}\n\nDetailedWeekViewSkin.ALL_DAY=Cel\\u00FD de\\u0148\n\nYearMonthViewSkin.MONTH_FORMAT=MMMM\nYearMonthViewSkin.TODAY=Dnes\nYearMonthViewSkin.YEAR_FORMAT=yyyy\n\nCalendarViewSkin.TOGGLE_SOURCE_TRAY=Kalend\\u00E1re\nCalendarViewSkin.TOGGLE_SHOW_DAY=De\\u0148\nCalendarViewSkin.TOOLTIP_SHOW_WEEK=Zobrazi\\u0165 t\\u00FD\\u017Ede\\u0148\nCalendarViewSkin.TOOLTIP_SHOW_MONTH=Zobrazi\\u0165 mesiac\nCalendarViewSkin.TOOLTIP_SHOW_YEAR=Zobrazi\\u0165 rok\nCalendarViewSkin.PROMPT_SEARCH_FIELD=H\\u013Eada\\u0165\nCalendarViewSkin.TOGGLE_SHOW_WEEK=T\\u00FD\\u017Ede\\u0148\nCalendarViewSkin.TOGGLE_SHOW_MONTH=Mesiac\nCalendarViewSkin.TOGGLE_SHOW_YEAR=Rok\nCalendarViewSkin.TOOLTIP_SOURCE_TRAY=Zobrazi\\u0165 zoznam v\\u0161etk\\u00FDch kalend\\u00E1rov\nCalendarViewSkin.TOOLTIP_ADD_CALENDAR=Prida\\u0165 kalend\\u00E1r\nCalendarViewSkin.TOOLTIP_PRINT=Tla\\u010D\nCalendarViewSkin.TOOLTIP_SHOW_DAY=Zobrazi\\u0165 de\\u0148\n\nPrintViewType.DAY_VIEW=De\\u0148\nPrintViewType.DAY_SINGULAR_CHRONO=de\\u0148\nPrintViewType.DAY_PLURAL_CHRONO=dni\nPrintViewType.WEEK_VIEW=T\\u00FD\\u017Ede\\u0148\nPrintViewType.WEEK_SINGULAR_CHRONO=t\\u00FD\\u017Ede\\u0148\nPrintViewType.WEEK_PLURAL_CHRONO=t\\u00FD\\u017Edne\nPrintViewType.MONTH_VIEW=Mesiac\nPrintViewType.MONTH_SINGULAR_CHRONO=mesiac\nPrintViewType.MONTH_PLURAL_CHRONO=mesiace\nPrintViewType.TO_LABEL=do\nPrintViewType.PAPER_TITLE_LABEL=Papier\nPrintViewType.TIME_RANGE_TITLE_LABEL=\\u010Casov\\u00FD rozsah\nPrintViewType.SOURCE_VIEW_TITLE_LABEL=Kalend\\u00E1re\nPrintViewType.OPTIONS_TITLE_LABEL=Mo\\u017Enosti\n\nPaperViewSkin.VIEW_TYPE_LABEL=Zobrazi\\u0165\nPaperViewSkin.PAPER_LABEL=Papier\nPaperViewSkin.MARGIN_LABEL=Okraj\n\nOptionsViewSkin.ALL_DAY_EVENTS_LABEL=Celodenn\\u00E9 z\\u00E1znamy\nOptionsViewSkin.DETAILS_LABEL=Podrobnosti\nOptionsViewSkin.TIMED_EVENTS_LABEL=\\u010Casovan\\u00E9 polo\\u017Eky\nOptionsViewSkin.MINI_CALENDAR_LABEL=Mini kalend\\u00E1r\nOptionsViewSkin.CALENDAR_KEYS_LABEL=K\\u013E\\u00FA\\u010De kalend\\u00E1ra\nOptionsViewSkin.SWIMLANE_LAYOUT_LABEL=Rozlo\\u017Eenie st\\u013Apca\n\nTimeRangeViewSkin.START_LABEL=\\u0161tart\nTimeRangeViewSkin.END_LABEL=Koniec\nTimeRangeViewSkin.PERIOD_LABEL_SINGULAR= vytla\\u010D\\u00ED sa {0} {1}\nTimeRangeViewSkin.PERIOD_LABEL_PLURAL= vytla\\u010D\\u00ED sa {0} {1}\n\nTimeRangeFieldValue.TODAY_LABEL=Dnes\nTimeRangeFieldValue.TOMORROW=Zajtra\nTimeRangeFieldValue.ON_DATE_LABEL=D\\u00E1tum\nTimeRangeFieldValue.THIS_WEEK_LABEL=Tento t\\u00FD\\u017Ede\\u0148\nTimeRangeFieldValue.NEXT_WEEK_LABEL=Bud\\u00FAci t\\u00FD\\u017Ede\\u0148\nTimeRangeFieldValue.ON_WEEK_NUMBER_LABEL=V t\\u00FD\\u017Edni\nTimeRangeFieldValue.THIS_MONTH_LABEL=Tento mesiac\nTimeRangeFieldValue.NEXT_MONTH_LABEL=Bud\\u00FAci mesiac\nTimeRangeFieldValue.JANUARY_LABEL=Janu\\u00E1r\nTimeRangeFieldValue.FEBRUARY_LABEL=Febru\\u00E1r\nTimeRangeFieldValue.MARCH_LABEL=Marec\nTimeRangeFieldValue.APRIL_LABEL=Apr\\u00EDl\nTimeRangeFieldValue.MAY_LABEL=M\\u00E1j\nTimeRangeFieldValue.JUNE_LABEL=J\\u00FAn\nTimeRangeFieldValue.JULY_LABEL=J\\u00FAl\nTimeRangeFieldValue.AUGUST_LABEL=August\nTimeRangeFieldValue.SEPTEMBER_LABEL=September\nTimeRangeFieldValue.OCTOBER_LABEL=Október\nTimeRangeFieldValue.NOVEMBER_LABEL=November\nTimeRangeFieldValue.DECEMBER_LABEL=December\nTimeRangeFieldValue.AFTER_LABEL=Po\n\nPreviewPaneSkin.ZOOM_LABEL=Pribl\\u00ED\\u017Eenie\n\nPrintView.TITLE_LABEL=Tla\\u010D\nPrintView.CONTINUE_BUTTON=Pokra\\u010Dova\\u0165\nPrintView.CANCEL_BUTTON=Zru\\u0161i\\u0165\nPrintView.NO_PRINTERS = Nedostupn\\u00E1 \\u017Eiadna tla\\u010Diare\\u0148\nPrintView.ERROR_NO_PRINTER = Nie je mo\\u017En\\u00E9 tla\\u010Di\\u0165, v tomto syst\\u00E9me nie je \\u017Eiadna tla\\u010Diare\\u0148.\n\nMargin.DEFAULT=Predvolen\\u00E9\nMargin.MINIMUM=Minimum\nMargin.CUSTOM=Vlastn\\u00E9\n\nMarginSelector.TOP=Hore\nMarginSelector.RIGHT=Vpravo\nMarginSelector.BOTTOM=Dole\nMarginSelector.LEFT=V\\u013Eavo\n\nSourceView.DISABLE_ALL=Zapn\\u00FA\\u0165 v\\u0161etko\nSourceView.ENABLE_ALL=Vypn\\u00FA\\u0165 v\\u0161etko\n\nMonthSheetView.ADD_NEW_EVENT=Prida\\u0165 nov\\u00FA udalos\\u0165\nMonthSheetView.STANDARD_CELLS=\\u0161tandard\nMonthSheetView.USAGE_CELLS=Pou\\u017Eitie\nMonthSheetView.DETAIL_CELLS=Detail\nMonthSheetView.BADGE_CELLS=Po\\u010D\\u00EDtadlo\n"
  },
  {
    "path": "CalendarFXView/src/test/java/com/calendarfx/model/CalendarTest.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.Matchers.not;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class CalendarTest {\n\n    private final Calendar calendar = new Calendar();\n    private Entry<String> recurrenceSourceEntry;\n    private Entry<String> recurrence;\n\n    @Before\n    public void setup() {\n        LocalDate today = LocalDate.now();\n        recurrenceSourceEntry = new Entry<>(\"Recurrence Source\");\n        recurrenceSourceEntry.setInterval(recurrenceSourceEntry.getInterval().withEndDate(LocalDate.now().plusDays(30)));\n        recurrenceSourceEntry.setRecurrenceRule(\"RRULE:FREQ=DAILY;\");\n        recurrenceSourceEntry.setCalendar(calendar);\n        Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(today, today, ZoneId.systemDefault());\n        recurrence = (Entry<String>) entries.get(today).get(0);\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceTitle() {\n        // given\n        String newTitle = \"New Title\";\n\n        // when\n        recurrence.setTitle(newTitle);\n\n        // then\n        assertThat(recurrenceSourceEntry.getTitle(), is(equalTo(newTitle)));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceRecurrenceRule() {\n        // given\n        String newRule = \"RRULE:FREQ=WEEKLY;\";\n\n        // when\n        recurrence.setRecurrenceRule(newRule);\n\n        // then\n        assertThat(recurrenceSourceEntry.getRecurrenceRule(),\n                is(equalTo(newRule)));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceInterval() {\n        // given\n        Interval oldInterval = recurrenceSourceEntry.getInterval();\n        Interval newInterval = new Interval(LocalDate.now().plusDays(1),\n                LocalTime.of(10, 00), LocalDate.now().plusDays(1),\n                LocalTime.of(13, 45), ZoneId.of(\"UTC\"));\n\n        // when\n        recurrence.setInterval(newInterval);\n\n        // then\n        assertThat(newInterval, is(not(equalTo(oldInterval))));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceCalendar() {\n        // given\n        Calendar newCalendar = new Calendar();\n\n        // when\n        recurrence.setCalendar(newCalendar);\n\n        // then\n        assertThat(recurrenceSourceEntry.getCalendar(),\n                is(equalTo(newCalendar)));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceFullDay() {\n        // given\n        recurrenceSourceEntry.setFullDay(false);\n\n        // when\n        recurrence.setFullDay(true);\n\n        // then\n        assertThat(recurrenceSourceEntry.isFullDay(), is(true));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceLocation() {\n        // given\n        String newLocation = \"New York\";\n\n        // when\n        recurrence.setLocation(newLocation);\n\n        // then\n        assertThat(recurrenceSourceEntry.getLocation(), is(equalTo(newLocation)));\n    }\n\n    @Test\n    public void shouldUpdateRecurrenceSourceUserObject() {\n        // given\n        String newUserObject = \"My User Object\";\n\n        // when\n        recurrence.setUserObject(newUserObject);\n\n        // then\n        assertThat(recurrenceSourceEntry.getUserObject(), is(equalTo(newUserObject)));\n    }\n\n    @Test\n    public void shouldReturnEntriesForSearch() throws Exception {\n        // given\n        Entry<?> entry1 = new Entry<>(\"xxxAAA\");\n        Entry<?> entry2 = new Entry<>(\"AAAxXx\");\n        Entry<?> entry3 = new Entry<>(\"AAxXXAAA\");\n        Entry<?> entryA = new Entry<>(\"ttTAAA\");\n        Entry<?> entryB = new Entry<>(\"AAATTt\");\n        Entry<?> entryC = new Entry<>(\"AAAtttAAA\");\n\n        entry1.setCalendar(calendar);\n        entryA.setCalendar(calendar);\n        entry2.setCalendar(calendar);\n        entryB.setCalendar(calendar);\n        entry3.setCalendar(calendar);\n        entryC.setCalendar(calendar);\n\n        // when\n        List<Entry<?>> entries = calendar.findEntries(\"xxx\");\n\n        // then\n        assertThat(entries.size(), is(equalTo(3)));\n        assertThat(entries.contains(entry1), is(true));\n        assertThat(entries.contains(entry2), is(true));\n        assertThat(entries.contains(entry3), is(true));\n        assertThat(entries.contains(entryA), is(false));\n        assertThat(entries.contains(entryB), is(false));\n        assertThat(entries.contains(entryC), is(false));\n    }\n\n    @Test\n    public void shouldReturnEntriesForTimeInterval() throws Exception {\n        // given\n        Entry<?> entry1 = new Entry<>();\n        Entry<?> entry2 = new Entry<>();\n        Entry<?> entry3 = new Entry<>();\n        Entry<?> entryA = new Entry<>();\n        Entry<?> entryB = new Entry<>();\n        Entry<?> entryC = new Entry<>();\n\n        entry1.changeStartDate(LocalDate.now());\n        entry1.changeEndDate(LocalDate.now().plusDays(10));\n\n        entry2.changeStartDate(LocalDate.now().plusDays(7));\n        entry2.changeEndDate(LocalDate.now().plusDays(9));\n\n        entry3.changeStartDate(LocalDate.now().plusDays(4));\n        entry3.changeEndDate(LocalDate.now().plusDays(6));\n\n        entryA.changeStartDate(LocalDate.now().minusDays(7));\n        entryA.changeEndDate(LocalDate.now().minusDays(10));\n\n        entryB.changeStartDate(LocalDate.now().minusDays(7));\n        entryB.changeEndDate(LocalDate.now().minusDays(9));\n\n        entryC.changeStartDate(LocalDate.now().plusDays(40));\n        entryC.changeEndDate(LocalDate.now().plusDays(60));\n\n        entry1.setCalendar(calendar);\n        entryA.setCalendar(calendar);\n        entry2.setCalendar(calendar);\n        entryB.setCalendar(calendar);\n        entry3.setCalendar(calendar);\n        entryC.setCalendar(calendar);\n\n        // when\n        Map<LocalDate, List<Entry<?>>> entries = calendar.findEntries(\n                LocalDate.now(), LocalDate.now().plusDays(10),\n                ZoneId.systemDefault());\n\n        // then\n        assertThat(entries.keySet().size(), is(equalTo(11)));\n\n        // entry1 is 11 days long: 0, 1, 2, ..., 10\n        for (int i = 0; i <= 10; i++) {\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entry1),\n                    is(true));\n\n            // A, B, C do not show up on any of these days\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entryA),\n                    is(false));\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entryB),\n                    is(false));\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entryC),\n                    is(false));\n        }\n\n        // entry2 is 3 days long: 7, 8, 9\n        for (int i = 7; i <= 9; i++) {\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entry2),\n                    is(true));\n        }\n\n        // entry3 is 3 days long: 4, 5, 6\n        for (int i = 4; i <= 6; i++) {\n            assertThat(\n                    entries.get(LocalDate.now().plusDays(i)).contains(entry3),\n                    is(true));\n        }\n    }\n\n    @Test\n    public void shouldBeShowing() {\n        // when\n        boolean showing = recurrenceSourceEntry.isShowing(LocalDate.now(), LocalDate.now().plusDays(1), ZoneId.systemDefault());\n\n        // then\n        assertThat(showing, is(true));\n    }\n\n    @Test\n    public void shouldNotBeShowing() {\n        // when\n        boolean showing = recurrenceSourceEntry.isShowing(LocalDate.now().minusDays(100), LocalDate.now().minusDays(90), ZoneId.systemDefault());\n\n        // then\n        assertThat(showing, is(false));\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/test/java/com/calendarfx/model/EntryTest.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport static java.lang.Boolean.TRUE;\nimport static org.hamcrest.Matchers.empty;\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.Matchers.not;\nimport static org.hamcrest.Matchers.nullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class EntryTest {\n\n    private final Entry<String> entry = new Entry<>();\n\n    private final Calendar calendar = new Calendar();\n\n    @Before\n    public void setup() {\n        entry.setLocation(\"Adliswil\");\n        entry.setUserObject(\"My User Object\");\n        entry.setZoneId(ZoneId.of(\"UTC\"));\n        entry.setCalendar(calendar);\n    }\n\n    @Test\n    public void shouldNotHaveStyles() {\n\n        // when\n        boolean result = entry.hasStyleClass();\n\n        // then\n        assertThat(result, is(equalTo(false)));\n    }\n\n    @Test\n    public void shouldHaveStylesAfterAdding() {\n\n        // when\n        entry.getStyleClass().add(\"mystyle\");\n\n        // then\n        assertThat(entry.hasStyleClass(), is(equalTo(true)));\n    }\n\n    @Test\n    public void shouldNotHaveProperties() {\n\n        // when\n        boolean result = entry.hasProperties();\n\n        // then\n        assertThat(result, is(equalTo(false)));\n    }\n\n    @Test\n    public void shouldHaveProperties() {\n        // when\n        entry.getProperties().put(\"mykey\", \"myvalue\");\n\n        // then\n        assertThat(entry.hasProperties(), is(equalTo(true)));\n    }\n\n    @Test\n    public void shouldBeEqual() {\n        // given\n        Entry entryA = new Entry();\n        Entry entryB = new Entry();\n\n        entryA.setId(\"ID\");\n        entryB.setId(\"ID\");\n\n        // when\n        boolean equal = entryA.equals(entryB);\n\n        // then\n        assertThat(equal, is(true));\n    }\n\n    @Test\n    public void shouldBeEqualRecurrences() {\n        // given\n        Entry<String> entryA = new Entry<>();\n        Entry<String> entryB = new Entry<>();\n\n        String id = UUID.randomUUID().toString();\n\n        entryA.setId(id);\n        entryB.setId(id);\n\n        String recurrenceId = ZonedDateTime.now().toString();\n\n        entryA.getProperties().put(\"com.calendarfx.recurrence.id\", recurrenceId);\n        entryB.getProperties().put(\"com.calendarfx.recurrence.id\", recurrenceId);\n\n        // when\n        boolean equal = entryA.equals(entryB);\n\n        // then\n        assertThat(equal, is(true));\n    }\n\n    @Test\n    public void shouldNotBeEqualWithSameRecurrenceIdAndId() {\n        // given\n        Entry<String> entryA = new Entry<>();\n        Entry<String> entryB = new Entry<>();\n\n        String id = UUID.randomUUID().toString();\n\n        entryA.setId(id);\n        entryB.setId(id);\n\n        String recurrenceId = ZonedDateTime.now().toString();\n\n        entryA.getProperties().put(\"com.calendarfx.recurrence.id\",\n                recurrenceId);\n        entryB.getProperties().put(\"com.calendarfx.recurrence.id\",\n                recurrenceId);\n\n        // when\n        boolean equal = entryA.equals(entryB);\n\n        int entryAHashcode = entryA.hashCode();\n        int entryBHashcode = entryB.hashCode();\n\n        Set<Entry<String>> mySet = new HashSet<>();\n        mySet.add(entryA);\n\n        // then\n        assertThat(entryA == entryB, is(false)); // two different instances\n        assertThat(equal, is(true));\n        assertThat(entryAHashcode, is(equalTo(entryBHashcode)));\n        assertThat(mySet.contains(entryA), is(true));\n        assertThat(mySet.contains(entryB), is(true));\n    }\n\n    @Test\n    public void shouldNotBeEqualWithSameRecurrenceIdButDifferentId() {\n        // given\n        Entry<String> entryA = new Entry<>();\n        Entry<String> entryB = new Entry<>();\n\n        entryA.setId(UUID.randomUUID().toString());\n        entryB.setId(UUID.randomUUID().toString());\n\n        String recurrenceId = ZonedDateTime.now().toString();\n\n        entryA.getProperties().put(\"com.calendarfx.recurrence.id\",\n                recurrenceId);\n        entryB.getProperties().put(\"com.calendarfx.recurrence.id\",\n                recurrenceId);\n\n        // when\n        boolean equal = entryA.equals(entryB);\n\n        int entryAHashcode = entryA.hashCode();\n        int entryBHashcode = entryB.hashCode();\n\n        Set<Entry<String>> mySet = new HashSet<>();\n        mySet.add(entryA);\n\n        // then\n        assertThat(entryA == entryB, is(false)); // two different instances\n        assertThat(equal, is(false));\n        assertThat(entryAHashcode, is(not(equalTo(entryBHashcode))));\n        assertThat(mySet.contains(entryA), is(true));\n        assertThat(mySet.contains(entryB), is(false));\n    }\n\n    @Test\n    public void shouldMakeRecurrence() {\n        // given\n        Entry<String> recurrence = new Entry<>();\n\n        // when\n        recurrence.getProperties().put(\"com.calendarfx.recurrence.source\",\n                entry);\n\n        // then\n        assertThat(recurrence.isRecurrence(), is(true));\n    }\n\n    @Test\n    public void shouldSetRecurrenceId() {\n        // given\n        Entry<String> recurrence = new Entry<>();\n\n        // when\n        recurrence.getProperties().put(\"com.calendarfx.recurrence.id\", \"xyz\");\n\n        // then\n        assertThat(recurrence.getRecurrenceId(), is(equalTo(\"xyz\")));\n    }\n\n    @Test\n    public void shouldSetRecurrenceRule() {\n        // given\n        String rule = \"RRULE:FREQ=DAILY;\";\n\n        // when\n        entry.setRecurrenceRule(rule);\n\n        // then\n        assertThat(entry.getRecurrenceRule(), is(equalTo(rule)));\n        assertThat(entry.getRecurrenceEnd(), is(equalTo(LocalDate.MAX)));\n    }\n\n    @Test\n    public void shouldReturnRecurrencesDaily() {\n        // given\n        final String rule = \"RRULE:FREQ=DAILY;\";\n        entry.setRecurrenceRule(rule);\n\n        final int days = 30;\n        entry.changeStartDate(LocalDate.now());\n        entry.changeEndDate(LocalDate.now());\n        entry.changeStartTime(LocalTime.of(10, 0));\n        entry.changeEndTime(LocalTime.of(12, 0));\n\n        // when\n        Map<LocalDate, List<Entry<?>>> result = calendar.findEntries(\n                LocalDate.now(), LocalDate.now().plusDays(days),\n                ZoneId.systemDefault());\n\n        // then\n        for (int currentDay = 0; currentDay < days; currentDay++) {\n            List<Entry<?>> entryList = result\n                    .get(LocalDate.now().plusDays(currentDay));\n\n            assertThat(entryList, is(not(empty())));\n            assertThat(entryList.size(), is(equalTo(1)));\n\n            Entry<?> recurrence = entryList.get(0);\n\n            assertThat(recurrence.getStartDate(),\n                    is(equalTo(entry.getStartDate().plusDays(currentDay))));\n            assertThat(recurrence.getEndDate(),\n                    is(equalTo(entry.getEndDate().plusDays(currentDay))));\n            assertThat(recurrence.getRecurrenceEnd(),\n                    is(equalTo(LocalDate.MAX)));\n            assertThat(recurrence.getRecurrenceRule(), is(equalTo(rule)));\n\n            assertThatRecurrenceConfiguredLikeSource(recurrence);\n        }\n    }\n\n    @Test\n    public void shouldReturnRecurrencesEveryOtherDayForOneMonth() {\n        // given\n        final String rule = \"RRULE:FREQ=DAILY;INTERVAL=2;\";\n        entry.setRecurrenceRule(rule);\n\n        final int days = 30;\n        entry.changeStartDate(LocalDate.now());\n        entry.changeEndDate(LocalDate.now());\n        entry.changeStartTime(LocalTime.of(10, 0));\n        entry.changeEndTime(LocalTime.of(12, 0));\n\n        // when\n        Map<LocalDate, List<Entry<?>>> result = calendar.findEntries(\n                LocalDate.now(), LocalDate.now().plusDays(days),\n                ZoneId.systemDefault());\n\n        // then\n        for (int currentDay = 0; currentDay < days; currentDay++) {\n            if (currentDay % 2 == 1) {\n                /*\n                 * We do not expect results for odd days\n                 */\n                List<Entry<?>> entryList = result\n                        .get(LocalDate.now().plusDays(currentDay));\n                assertThat(entryList, is(nullValue()));\n            } else {\n                List<Entry<?>> entryList = result\n                        .get(LocalDate.now().plusDays(currentDay));\n\n                assertThat(entryList, is(not(empty())));\n                assertThat(entryList.size(), is(equalTo(1)));\n\n                Entry<?> recurrence = entryList.get(0);\n\n                assertThat(recurrence.getStartDate(),\n                        is(equalTo(entry.getStartDate().plusDays(currentDay))));\n                assertThat(recurrence.getEndDate(),\n                        is(equalTo(entry.getEndDate().plusDays(currentDay))));\n                assertThat(recurrence.getRecurrenceEnd(),\n                        is(equalTo(LocalDate.MAX)));\n                assertThat(recurrence.getRecurrenceRule(), is(equalTo(rule)));\n\n                assertThatRecurrenceConfiguredLikeSource(recurrence);\n            }\n        }\n    }\n\n    @Test\n    public void shouldReturnRecurrencesWeekly() {\n        // given\n        final String rule = \"RRULE:FREQ=WEEKLY;\";\n        entry.setRecurrenceRule(rule);\n\n        final int weeks = 10;\n        entry.changeStartDate(LocalDate.now());\n        entry.changeEndDate(LocalDate.now());\n        entry.changeStartTime(LocalTime.of(10, 0));\n        entry.changeEndTime(LocalTime.of(12, 0));\n\n        // when\n        Map<LocalDate, List<Entry<?>>> result = calendar.findEntries(\n                LocalDate.now(), LocalDate.now().plusWeeks(weeks),\n                ZoneId.systemDefault());\n\n        // then\n        for (int currentWeek = 0; currentWeek < weeks; currentWeek++) {\n            List<Entry<?>> entryList = result\n                    .get(LocalDate.now().plusWeeks(currentWeek));\n\n            assertThat(entryList, is(not(empty())));\n            assertThat(entryList.size(), is(equalTo(1)));\n\n            Entry<?> recurrence = entryList.get(0);\n\n            assertThat(recurrence.getStartDate(),\n                    is(equalTo(entry.getStartDate().plusWeeks(currentWeek))));\n            assertThat(recurrence.getEndDate(),\n                    is(equalTo(entry.getEndDate().plusWeeks(currentWeek))));\n            assertThat(recurrence.getRecurrenceEnd(),\n                    is(equalTo(LocalDate.MAX)));\n            assertThat(recurrence.getRecurrenceRule(), is(equalTo(rule)));\n\n            assertThatRecurrenceConfiguredLikeSource(recurrence);\n        }\n    }\n\n    @Test\n    public void shouldReturnRecurrencesMonthly() {\n        // given\n        final String rule = \"RRULE:FREQ=MONTHLY;\";\n        entry.setRecurrenceRule(rule);\n\n        /*\n         * We use 2015/1/1 as a fixed date to ensure that we do not run into\n         * issues with February having 28 or 29 days.\n         */\n\n        final int months = 10;\n        entry.changeStartDate(LocalDate.of(2015, 1, 1));\n        entry.changeEndDate(LocalDate.of(2015, 1, 1));\n        entry.changeStartTime(LocalTime.of(10, 0));\n        entry.changeEndTime(LocalTime.of(12, 0));\n\n\n        // when\n        Map<LocalDate, List<Entry<?>>> result = calendar.findEntries(\n                LocalDate.of(2015, 1, 1), LocalDate.of(2015, 1, 1).plusMonths(months),\n                ZoneId.systemDefault());\n\n        // then\n        for (int currentMonth = 0; currentMonth < months; currentMonth++) {\n            LocalDate date = LocalDate.of(2015, 1, 1).plusMonths(currentMonth);\n            List<Entry<?>> entryList = result\n                    .get(date);\n\n            assertThat(entryList, is(not(empty())));\n            assertThat(entryList.size(), is(equalTo(1)));\n\n            Entry<?> recurrence = entryList.get(0);\n\n            assertThat(recurrence.getStartDate(),\n                    is(equalTo(entry.getStartDate().plusMonths(currentMonth))));\n            assertThat(recurrence.getEndDate(),\n                    is(equalTo(entry.getEndDate().plusMonths(currentMonth))));\n            assertThat(recurrence.getRecurrenceEnd(),\n                    is(equalTo(LocalDate.MAX)));\n            assertThat(recurrence.getRecurrenceRule(), is(equalTo(rule)));\n\n            assertThatRecurrenceConfiguredLikeSource(recurrence);\n        }\n    }\n\n    @Test\n    public void shouldReturnRecurrencesYearly() {\n        // given\n        final String rule = \"RRULE:FREQ=YEARLY;\";\n        entry.setRecurrenceRule(rule);\n\n        final int years = 10;\n        entry.changeStartDate(LocalDate.now());\n        entry.changeEndDate(LocalDate.now());\n        entry.changeStartTime(LocalTime.of(10, 0));\n        entry.changeEndTime(LocalTime.of(12, 0));\n\n        // when\n        Map<LocalDate, List<Entry<?>>> result = calendar.findEntries(\n                LocalDate.now(), LocalDate.now().plusYears(years),\n                ZoneId.systemDefault());\n\n        // then\n        for (int currentYear = 0; currentYear < years; currentYear++) {\n            List<Entry<?>> entryList = result\n                    .get(LocalDate.now().plusYears(currentYear));\n\n            assertThat(entryList, is(not(empty())));\n            assertThat(entryList.size(), is(equalTo(1)));\n\n            Entry<?> recurrence = entryList.get(0);\n\n            assertThat(recurrence.getStartDate(),\n                    is(equalTo(entry.getStartDate().plusYears(currentYear))));\n            assertThat(recurrence.getEndDate(),\n                    is(equalTo(entry.getEndDate().plusYears(currentYear))));\n            assertThat(recurrence.getRecurrenceEnd(),\n                    is(equalTo(LocalDate.MAX)));\n            assertThat(recurrence.getRecurrenceRule(), is(equalTo(rule)));\n\n            assertThatRecurrenceConfiguredLikeSource(recurrence);\n        }\n    }\n\n    private void assertThatRecurrenceConfiguredLikeSource(Entry<?> recurrence) {\n        assertThat(recurrence.getRecurrenceSourceEntry(), is(equalTo(entry)));\n        assertThat(recurrence.isRecurrence(), is(true));\n        assertThat(recurrence.isRecurring(), is(true));\n        assertThat(recurrence.getStartTime(),\n                is(equalTo(entry.getStartTime())));\n        assertThat(recurrence.getEndTime(), is(equalTo(entry.getEndTime())));\n        assertThat(recurrence.getTitle(), is(equalTo(entry.getTitle())));\n        assertThat(recurrence.isFullDay(), is(equalTo(entry.isFullDay())));\n        assertThat(recurrence.isMultiDay(), is(equalTo(entry.isMultiDay())));\n        assertThat(recurrence.getMinimumDuration(),\n                is(equalTo(entry.getMinimumDuration())));\n        assertThat(recurrence.getLocation(), is(equalTo(entry.getLocation())));\n        assertThat(recurrence.getUserObject(),\n                is(equalTo(entry.getUserObject())));\n        assertThat(recurrence.getZoneId(), is(equalTo(entry.getZoneId())));\n    }\n\n    @Test\n    public void shouldChangeTitle() {\n        // given\n        String title = \"New Title\";\n\n        // when\n        entry.setTitle(title);\n\n        // then\n        assertThat(entry.getTitle(), is(equalTo(title)));\n    }\n\n    @Test\n    public void shouldChangeInterval() {\n        LocalDate startDate = LocalDate.now().plusDays(1);\n        LocalDate endDate = LocalDate.now().plusDays(2);\n\n        LocalTime startTime = LocalTime.now();\n        LocalTime endTime = LocalTime.now().plusHours(10);\n\n        Interval interval = new Interval(startDate, startTime, endDate, endTime,\n                ZoneId.of(\"UTC\"));\n\n        entry.setInterval(interval);\n\n        assertThat(entry.getStartDate(), is(equalTo(startDate)));\n        assertThat(entry.getStartTime(), is(equalTo(startTime)));\n        assertThat(entry.getEndDate(), is(equalTo(endDate)));\n        assertThat(entry.getEndTime(), is(equalTo(endTime)));\n        assertThat(entry.getZoneId(), is(equalTo(ZoneId.of(\"UTC\"))));\n    }\n\n    @Test\n    public void shouldChangeStartTime() {\n        // given\n        LocalTime time = entry.getStartTime().plusHours(1);\n\n        // when\n        entry.changeStartTime(time);\n\n        // then\n        assertThat(entry.getStartTime(), is(equalTo(time)));\n    }\n\n    @Test\n    public void shouldChangeEndTime() {\n        // given\n        LocalTime time = entry.getEndTime().plusHours(1);\n\n        // when\n        entry.changeEndTime(time);\n\n        // then\n        assertThat(entry.getEndTime(), is(equalTo(time)));\n    }\n\n    @Test\n    public void shouldChangeStartDate() {\n        // given\n        LocalDate date = entry.getStartDate().plusDays(1);\n\n        // when\n        entry.changeStartDate(date);\n\n        // then\n        assertThat(entry.getStartDate(), is(equalTo(date)));\n    }\n\n    @Test\n    public void shouldChangeEndDate() {\n        // given\n        LocalDate date = entry.getEndDate().plusDays(1);\n\n        // when\n        entry.changeEndDate(date);\n\n        // then\n        assertThat(entry.getEndDate(), is(equalTo(date)));\n    }\n\n    @Test\n    public void shouldChangeZoneId() {\n        // given\n        ZoneId zoneId = ZoneId.of(\"GMT\");\n\n        // when\n        entry.setZoneId(zoneId);\n\n        // then\n        assertThat(entry.getZoneId(), is(equalTo(zoneId)));\n    }\n\n    @Test\n    public void shouldChangeUserObject() {\n        // given\n        String userObject = \"Hello World\";\n\n        // when\n        entry.setUserObject(userObject);\n\n        // then\n        assertThat(entry.getUserObject(), is(equalTo(userObject)));\n    }\n\n    @Test\n    public void shouldChangeFullDay() {\n        // given\n        boolean fullDay = true;\n\n        // when\n        entry.setFullDay(fullDay);\n\n        // then\n        assertThat(entry.isFullDay(), is(equalTo(TRUE)));\n    }\n\n    @Test\n    public void shouldChangeCalendar() {\n        // given\n        Calendar newCalendar = new Calendar();\n\n        // when\n        entry.setCalendar(newCalendar);\n\n        // then\n        assertThat(entry.getCalendar(), is(equalTo(newCalendar)));\n    }\n\n    @Test\n    public void shouldReturnMultiDay1() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = st.plusDays(1);\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        // when\n        boolean multiDay = entry.isMultiDay();\n\n        // then\n        assertThat(multiDay, is(true));\n    }\n\n    @Test\n    public void shouldReturnMultiDay2() {\n        // given\n        LocalDate st = LocalDate.now().minusDays(4);\n        LocalDate et = st.plusDays(4);\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        // when\n        boolean multiDay = entry.isMultiDay();\n\n        // then\n        assertThat(multiDay, is(true));\n    }\n\n    @Test\n    public void shouldNotReturnMultiDay() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = st;\n\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        // when\n        boolean multiDay = entry.isMultiDay();\n\n        // then\n        assertThat(multiDay, is(false));\n    }\n\n    @Test\n    public void shouldIntersectWithTimeInterval() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = LocalDate.now().plusDays(7);\n\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        ZonedDateTime zst = ZonedDateTime.now().plusDays(2);\n        ZonedDateTime zet = ZonedDateTime.now().plusDays(4);\n\n        // when\n        boolean intersects = entry.intersects(zst, zet);\n\n        // then\n        assertThat(intersects, is(true));\n    }\n\n    @Test\n    public void shouldNotIntersectWithTimeInterval() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = LocalDate.now().plusDays(7);\n\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        ZonedDateTime zst = ZonedDateTime.now().plusDays(9);\n        ZonedDateTime zet = ZonedDateTime.now().plusDays(12);\n\n        // when\n        boolean intersects = entry.intersects(zst, zet);\n\n        // then\n        assertThat(intersects, is(false));\n    }\n\n    @Test\n    public void shouldIntersectWithTimeOtherEntry() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = LocalDate.now().plusDays(7);\n\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        Entry<?> entry2 = new Entry<>();\n\n        st = LocalDate.now().plusDays(3);\n        et = LocalDate.now().plusDays(5);\n\n        entry2.changeStartDate(st);\n        entry2.changeEndDate(et);\n\n        // when\n        boolean intersects = entry.intersects(entry2);\n\n        // then\n        assertThat(intersects, is(true));\n\n        /*-- reverse operation --*/\n\n        // when\n        intersects = entry2.intersects(entry);\n\n        // then\n        assertThat(intersects, is(true));\n    }\n\n    @Test\n    public void shouldNotIntersectWithTimeOtherEntry() {\n        // given\n        LocalDate st = LocalDate.now();\n        LocalDate et = LocalDate.now().plusDays(7);\n\n        entry.changeStartDate(st);\n        entry.changeEndDate(et);\n\n        Entry<?> entry2 = new Entry<>();\n\n        st = LocalDate.now().plusDays(9);\n        et = LocalDate.now().plusDays(12);\n\n        entry2.changeStartDate(st);\n        entry2.changeEndDate(et);\n\n        // when\n        boolean intersects = entry.intersects(entry2);\n\n        // then\n        assertThat(intersects, is(false));\n\n        /*-- reverse operation --*/\n\n        // when\n        intersects = entry2.intersects(entry);\n\n        // then\n        assertThat(intersects, is(false));\n    }\n\n    @Test\n    public void shouldMatchSearchTerm() {\n        // given\n        String title = \"My Title\";\n        entry.setTitle(title);\n\n        // when\n        boolean match = entry.matches(\"My\");\n\n        // then\n        assertThat(match, is(true));\n    }\n\n    @Test\n    public void shouldSortBasedOnStartTime() {\n        // given\n        String title = \"My Title\";\n        entry.setTitle(title);\n\n        // when\n        boolean match = entry.matches(\"xxx\");\n\n        // then\n        assertThat(match, is(false));\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/test/java/com/calendarfx/model/IntervalTest.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.model;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\npublic class IntervalTest {\n\n    private Interval interval;\n\n    @Before\n    public void setup() {\n        interval = new Interval(LocalDate.now(), LocalTime.MIN, LocalDate.now(), LocalTime.MAX);\n    }\n\n    @Test\n    public void shouldBeEqual() {\n        // given\n        Interval interval1 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 15), LocalTime.of(23, 00));\n        Interval interval2 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 15), LocalTime.of(23, 00));\n\n        // when\n        boolean equal = interval1.equals(interval2);\n\n        // then\n        assertThat(equal, is(true));\n    }\n\n    @Test\n    public void shouldNotBeEqualBecauseOfDifferentLocalDate() {\n        // given\n        Interval interval1 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 15), LocalTime.of(23, 00));\n        Interval interval2 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 16), LocalTime.of(23, 00));\n\n        // when\n        boolean equal = interval1.equals(interval2);\n\n        // then\n        assertThat(equal, is(false));\n    }\n\n    @Test\n    public void shouldNotBeEqualBecauseOfDifferentLocalTime() {\n        // given\n        Interval interval1 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 15), LocalTime.of(21, 00));\n        Interval interval2 = new Interval(LocalDate.of(2017, 1, 14), LocalTime.of(10, 00), LocalDate.of(2017, 1, 15), LocalTime.of(23, 00));\n\n        // when\n        boolean equal = interval1.equals(interval2);\n\n        // then\n        assertThat(equal, is(false));\n    }\n\n    @Test\n    public void shouldBeInitializedProperly() {\n        assertThat(interval.getStartDate(), is(equalTo(LocalDate.now())));\n        assertThat(interval.getEndDate(), is(equalTo(LocalDate.now())));\n\n        // we can not assert the end times as LocalTime.now() wil be different\n        // now\n    }\n\n    @Test\n    public void shouldChangeStartDate() {\n        // given\n        LocalDate newDate = LocalDate.now().minusDays(20);\n\n        // when\n        interval = interval.withStartDate(newDate);\n\n        // then\n        assertThat(interval.getStartDate(), is(equalTo(newDate)));\n    }\n\n    @Test\n    public void shouldChangeEndDate() {\n        // given\n        LocalDate newDate = LocalDate.now().plusDays(20);\n\n        // when\n        interval = interval.withEndDate(newDate);\n\n        // then\n        assertThat(interval.getEndDate(), is(equalTo(newDate)));\n    }\n\n    @Test\n    public void shouldChangeStartTime() {\n        // given\n        LocalTime newTime = LocalTime.of(10, 15);\n\n        // when\n        interval = interval.withStartTime(newTime);\n\n        // then\n        assertThat(interval.getStartTime(), is(equalTo(newTime)));\n    }\n\n    @Test\n    public void shouldChangeEndTime() {\n        // given\n        LocalTime newTime = LocalTime.of(10, 15);\n\n        // when\n        interval = interval.withEndTime(newTime);\n\n        // then\n        assertThat(interval.getEndTime(), is(equalTo(newTime)));\n    }\n\n    @Test\n    public void shouldChangeDuration() {\n        // given\n        Duration newDuration = Duration.ofMinutes(30);\n\n        // when\n        interval = interval.withDuration(newDuration);\n\n        // then\n        assertThat(interval.getDuration(), is(equalTo(newDuration)));\n        assertThat(interval.getEndTime(), is(equalTo(interval.getStartTime().plus(newDuration))));\n    }\n\n    @Test\n    public void shouldChangeZoneId() {\n        // given\n        ZoneId newZoneId = ZoneId.of(\"UTC\");\n\n        // when\n        interval = interval.withZoneId(newZoneId);\n\n        // then\n        assertThat(interval.getZoneId(), is(equalTo(newZoneId)));\n    }\n\n    @Test\n    public void shouldChangeAll() {\n        // given\n        LocalDate newStartDate = LocalDate.now().minusMonths(1);\n        LocalDate newEndDate = LocalDate.now().plusMonths(2);\n        LocalTime newStartTime = LocalTime.of(10, 15);\n        LocalTime newEndTime = LocalTime.of(10, 15);\n        ZoneId newZoneId = ZoneId.of(\"UTC\");\n\n        // when\n        interval = interval.withStartDate(newStartDate).withEndDate(newEndDate)\n                .withStartTime(newStartTime).withEndTime(newEndTime).withZoneId(newZoneId);\n\n        // then\n        assertThat(interval.getStartDate(), is(equalTo(newStartDate)));\n        assertThat(interval.getStartTime(), is(equalTo(newStartTime)));\n        assertThat(interval.getEndDate(), is(equalTo(newEndDate)));\n        assertThat(interval.getEndTime(), is(equalTo(newEndTime)));\n        assertThat(interval.getZoneId(), is(equalTo(newZoneId)));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldFailBecauseOfBadDates() {\n        // given\n        LocalDate startDate = LocalDate.now();\n        LocalDate endDate = LocalDate.now().minusDays(1);\n\n        // when\n        new Interval(startDate, LocalTime.now(), endDate, LocalTime.now(), ZoneId.systemDefault());\n\n        // then\n        // throw exception\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void shouldFailBecauseOfBadTimes() {\n        // given\n        LocalDate startDate = LocalDate.now();\n        LocalDate endDate = startDate;\n\n        // when\n        new Interval(startDate, LocalTime.now(), endDate, LocalTime.now().minusHours(1), ZoneId.systemDefault());\n\n        // then\n        // throw exception\n    }\n}\n"
  },
  {
    "path": "CalendarFXView/src/test/java/com/calendarfx/view/DateSelectionModelTests.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.view;\n\nimport com.calendarfx.view.DateSelectionModel.SelectionMode;\nimport org.junit.Test;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.hamcrest.Matchers.contains;\nimport static org.hamcrest.Matchers.empty;\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.hamcrest.Matchers.is;\nimport static org.hamcrest.Matchers.not;\nimport static org.hamcrest.Matchers.nullValue;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertThat;\nimport static org.junit.Assert.assertTrue;\n\npublic class DateSelectionModelTests {\n\n    @Test\n    public void shouldBeEmptySelection() {\n        // Given\n        DateSelectionModel model = new DateSelectionModel();\n\n        // Then\n        assertNotNull(model.getSelectedDates());\n        assertThat(model.getSelectedDates(), is(empty()));\n        assertTrue(model.isEmpty());\n        assertEquals(model.getSelectionMode(), SelectionMode.MULTIPLE_DATES);\n        assertThat(model.getLastSelected(), is(nullValue()));\n    }\n\n    @Test\n    public void shouldSelectOneSingleDate() {\n        // Given\n        LocalDate now = LocalDate.now();\n        DateSelectionModel model = new DateSelectionModel();\n\n        // When\n        model.select(now);\n\n        // Then\n        assertTrue(model.isSelected(now));\n        assertThat(model.getSelectedDates(), contains(now));\n        assertEquals(model.getLastSelected(), now);\n    }\n\n    @Test\n    public void shouldSelectDateAndClearSelection() {\n        // Given\n        LocalDate now = LocalDate.now();\n        DateSelectionModel model = new DateSelectionModel();\n\n        // When\n        model.select(now);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates(), contains(now));\n\n        // When\n        model.clear();\n\n        // Then\n        assertTrue(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(empty()));\n        assertThat(model.getLastSelected(), is(nullValue()));\n    }\n\n    @Test\n    public void shouldSelectAndDeselectDate() {\n        // Given\n        LocalDate now = LocalDate.now();\n        DateSelectionModel model = new DateSelectionModel();\n\n        // When\n        model.select(now);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates(), contains(now));\n        assertTrue(model.isSelected(now));\n\n        // When\n        model.deselect(now);\n\n        // Then\n        assertTrue(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(empty()));\n        assertThat(model.getLastSelected(), is(nullValue()));\n    }\n\n    @Test\n    public void shouldSelectDateRange() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE_RANGE);\n\n        // When\n        model.selectRange(now, dayAfterTomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(3)));\n        assertThat(model.getSelectedDates(), contains(now, tomorrow, dayAfterTomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(dayAfterTomorrow)));\n        assertTrue(model.isSelected(now));\n        assertTrue(model.isSelected(tomorrow));\n        assertTrue(model.isSelected(dayAfterTomorrow));\n    }\n\n    @Test\n    public void shouldSelectMultipleDates() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);\n        LocalDate oneYearAfter = dayAfterTomorrow.plusYears(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.MULTIPLE_DATES);\n\n        // When\n        model.selectRange(now, dayAfterTomorrow);\n        model.select(oneYearAfter);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(4)));\n        assertThat(model.getSelectedDates(), contains(now, tomorrow, dayAfterTomorrow, oneYearAfter));\n        assertThat(model.getLastSelected(), is(equalTo(oneYearAfter)));\n        assertTrue(model.isSelected(now));\n        assertTrue(model.isSelected(tomorrow));\n        assertTrue(model.isSelected(dayAfterTomorrow));\n        assertTrue(model.isSelected(oneYearAfter));\n    }\n\n    @Test\n    public void shouldChangeSelectionFromTodayToTomorrow() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE);\n\n        // When\n        model.select(now);\n        model.select(tomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(1)));\n        assertThat(model.getSelectedDates(), contains(tomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(tomorrow)));\n        assertFalse(model.isSelected(now));\n        assertTrue(model.isSelected(tomorrow));\n    }\n\n    @Test\n    public void shouldSelectOnlySingleDate_AttemptRangeSelection() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE);\n\n        // When\n        model.selectRange(now, dayAfterTomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(1)));\n        assertThat(model.getSelectedDates(), contains(dayAfterTomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(dayAfterTomorrow)));\n        assertFalse(model.isSelected(now));\n        assertFalse(model.isSelected(tomorrow));\n        assertTrue(model.isSelected(dayAfterTomorrow));\n    }\n\n    @Test\n    public void shouldSelectOnlySingleDate_AttemptMultipleSelection() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE);\n\n        // When\n        model.select(now);\n        model.selectUntil(dayAfterTomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(1)));\n        assertThat(model.getSelectedDates(), contains(dayAfterTomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(dayAfterTomorrow)));\n        assertFalse(model.isSelected(now));\n        assertFalse(model.isSelected(tomorrow));\n        assertTrue(model.isSelected(dayAfterTomorrow));\n    }\n\n    @Test\n    public void shouldSelectRangeTodayAndTomorrow() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE_RANGE);\n\n        // When\n        model.select(now);\n        model.select(tomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(2)));\n        assertThat(model.getSelectedDates(), contains(now, tomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(tomorrow)));\n        assertTrue(model.isSelected(now));\n        assertTrue(model.isSelected(tomorrow));\n    }\n\n    @Test\n    public void shouldSelectFromTodayUntilTomorrow() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate tomorrow = now.plusDays(1);\n        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE_RANGE);\n\n        // When\n        model.select(now);\n        model.selectUntil(dayAfterTomorrow);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(3)));\n        assertThat(model.getSelectedDates(), contains(now, tomorrow, dayAfterTomorrow));\n        assertThat(model.getLastSelected(), is(equalTo(dayAfterTomorrow)));\n        assertTrue(model.isSelected(now));\n        assertTrue(model.isSelected(tomorrow));\n        assertTrue(model.isSelected(dayAfterTomorrow));\n    }\n\n    @Test\n    public void shouldSelectFromTodayUntilNextTwoWeeks() {\n        // Given\n        LocalDate now = LocalDate.now();\n        LocalDate nextWeek = now.plusWeeks(1);\n        LocalDate nextWeekAfterNextWeek = nextWeek.plusWeeks(1);\n\n        List<LocalDate> days = new ArrayList<>();\n        LocalDate start = now;\n        while (start.isBefore(nextWeekAfterNextWeek) || start.isEqual(nextWeekAfterNextWeek)) {\n            days.add(start);\n            start = start.plusDays(1);\n        }\n\n        DateSelectionModel model = new DateSelectionModel();\n        model.setSelectionMode(SelectionMode.SINGLE_DATE_RANGE);\n\n        // When\n        model.select(now);\n        model.selectUntil(nextWeek);\n        model.selectUntil(nextWeekAfterNextWeek);\n\n        // Then\n        assertFalse(model.isEmpty());\n        assertThat(model.getSelectedDates(), is(not(empty())));\n        assertThat(model.getSelectedDates().size(), is(equalTo(days.size())));\n        assertThat(model.getLastSelected(), is(equalTo(nextWeekAfterNextWeek)));\n\n        for (LocalDate day : days) {\n            assertTrue(model.isSelected(day));\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXWeather/.gitignore",
    "content": "/target/\n*.iml"
  },
  {
    "path": "CalendarFXWeather/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>weather</artifactId>\n    <name>CalendarFXWeather</name>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.kordamp.ikonli</groupId>\n            <artifactId>ikonli-weathericons-pack</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.weather.WeatherApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "CalendarFXWeather/src/main/java/com/calendarfx/weather/WeatherApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.weather;\n\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.MonthSheetView;\nimport javafx.application.Application;\nimport javafx.beans.Observable;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\nimport org.kordamp.ikonli.javafx.FontIcon;\nimport org.kordamp.ikonli.weathericons.WeatherIcons;\n\nimport java.time.LocalDate;\n\npublic class WeatherApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        Calendar zurich = new Calendar(\"Zurich\");\n        CalendarSource calendarSource = new CalendarSource(\"Weather\");\n        calendarSource.getCalendars().addAll(zurich);\n\n        MonthSheetView sheetView = new MonthSheetView();\n        sheetView.setPadding(new Insets(20));\n        sheetView.setCellFactory(param -> new WeatherCell(param.getView(), param.getDate()));\n        sheetView.getCalendarSources().setAll(calendarSource);\n        sheetView.setContextMenu(null);\n\n        Scene scene = new Scene(sheetView);\n        primaryStage.setTitle(\"Weather Calendar\");\n        primaryStage.setScene(scene);\n        primaryStage.setWidth(1300);\n        primaryStage.setHeight(1000);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n    private static class WeatherCell extends MonthSheetView.SimpleDateCell {\n\n        private FontIcon icon = new FontIcon(WeatherIcons.DAY_SUNNY);\n\n        public WeatherCell(MonthSheetView view, LocalDate date) {\n            super(view, date);\n\n            weekNumberLabel.setVisible(false);\n\n            if (getDate() != null) {\n\n                switch ((int) (Math.random() * 9)) {\n                    case 0:\n                        icon = new FontIcon(WeatherIcons.DAY_SUNNY);\n                        break;\n                    case 1:\n                        icon = new FontIcon(WeatherIcons.DAY_RAIN);\n                        break;\n                    case 2:\n                        icon = new FontIcon(WeatherIcons.DAY_CLOUDY);\n                        break;\n                    case 3:\n                        icon = new FontIcon(WeatherIcons.DAY_FOG);\n                        break;\n                    case 4:\n                        icon = new FontIcon(WeatherIcons.DAY_LIGHTNING);\n                        break;\n                    case 5:\n                        icon = new FontIcon(WeatherIcons.DAY_HAIL);\n                        break;\n                    case 6:\n                        icon = new FontIcon(WeatherIcons.DAY_CLOUDY_HIGH);\n                        break;\n                    case 7:\n                        icon = new FontIcon(WeatherIcons.DAY_HAZE);\n                        break;\n                    default:\n                        icon = new FontIcon(WeatherIcons.DAY_SHOWERS);\n                        break;\n                }\n            }\n\n            getChildren().add(icon);\n\n            icon.setIconSize(14);\n            updateFillColor(icon);\n            FontIcon fIcon = icon;\n            getView().getDateSelectionModel().getSelectedDates().addListener((Observable it) -> updateFillColor(fIcon));\n        }\n\n        private void updateFillColor(FontIcon icon) {\n            if (getDate() != null && getDate().equals(getView().getToday()) || getView().getDateSelectionModel().isSelected(getDate())) {\n                icon.setIconColor(Color.WHITE);\n            } else {\n                icon.setIconColor(Color.CADETBLUE);\n            }\n        }\n\n        @Override\n        protected void layoutChildren() {\n            Insets insets = getInsets();\n\n            double top = insets.getTop();\n            double bottom = insets.getBottom();\n            double left = insets.getLeft();\n            double right = insets.getRight();\n\n            double w = getWidth();\n            double h = getHeight();\n\n            double availableHeight = h - top - bottom;\n\n            double ps1 = dayOfMonthLabel.prefWidth(-1);\n            double ps2 = dayOfWeekLabel.prefWidth(-1);\n            double ps3 = icon.prefWidth(-1);\n\n            dayOfMonthLabel.resizeRelocate(left, top, ps1, availableHeight);\n            dayOfWeekLabel.resizeRelocate(left + ps1, top, ps2, availableHeight);\n            icon.resizeRelocate(w - right - ps3, (availableHeight - icon.prefHeight(-1)) / 2, ps3, availableHeight);\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXWeather/src/main/java/com/calendarfx/weather/WeatherAppLauncher.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.weather;\n\npublic class WeatherAppLauncher {\n\n    public static void main(String[] args) {\n        System.setProperty(\"calendarfx.developer\", \"true\");\n        WeatherApp.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXWeather/src/main/java/module-info.java",
    "content": "module com.calendarfx.weather {\n    requires transitive javafx.graphics;\n\n    requires javafx.controls;\n    requires com.calendarfx.view;\n    requires org.kordamp.ikonli.javafx;\n    requires org.kordamp.ikonli.weathericons;\n\n    exports com.calendarfx.weather;\n}"
  },
  {
    "path": "CalendarFXiCal/.gitignore",
    "content": "/target\n*.iml\n"
  },
  {
    "path": "CalendarFXiCal/pom.xml",
    "content": "<!--\n  ~  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n  ~\n  ~  Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  you may not use this file except in compliance with the License.\n  ~  You may obtain a copy of the License at\n  ~\n  ~          http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~  Unless required by applicable law or agreed to in writing, software\n  ~  distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  See the License for the specific language governing permissions and\n  ~  limitations under the License.\n  -->\n\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n    <name>CalendarFXiCal</name>\n    <artifactId>ical</artifactId>\n\n    <parent>\n        <groupId>com.calendarfx</groupId>\n        <artifactId>calendar</artifactId>\n        <version>12.0.1</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <properties>\n        <maven.deploy.skip>true</maven.deploy.skip>\n    </properties>\n\n    <repositories>\n        <repository>\n            <id>sonatype</id>\n            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n        </repository>\n    </repositories>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.calendarfx</groupId>\n            <artifactId>view</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mnode.ical4j</groupId>\n            <artifactId>ical4j</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-maven-plugin</artifactId>\n                <version>${javafx.maven.plugin.version}</version>\n                <configuration>\n                    <mainClass>com.calendarfx.ical.ICalCalendarApp</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/ICalCalendarApp.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical;\n\nimport com.calendarfx.ical.model.ICalCalendar;\nimport com.calendarfx.ical.view.ICalWebSourceFactory;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.model.LoadEvent;\nimport com.calendarfx.util.LoggingDomain;\nimport com.calendarfx.view.CalendarView;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.concurrent.Task;\nimport javafx.scene.Scene;\nimport javafx.scene.image.ImageView;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\nimport net.fortuna.ical4j.util.MapTimeZoneCache;\nimport org.controlsfx.dialog.ProgressDialog;\n\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\npublic class ICalCalendarApp extends Application {\n\n    private static final ExecutorService executor = Executors\n            .newCachedThreadPool(runnable -> {\n                Thread thread = new Thread(runnable, \"ICalCalendar Load Thread\");\n                thread.setDaemon(true);\n                thread.setPriority(Thread.MIN_PRIORITY);\n                return thread;\n            });\n\n    @Override\n    public void start(Stage primaryStage) {\n        LoggingDomain.CONFIG.info(\"Java version: \" + System.getProperty(\"java.version\"));\n\n        System.setProperty(\"ical4j.unfolding.relaxed\", \"true\");\n        System.setProperty(\"ical4j.parsing.relaxed\", \"true\");\n        System.setProperty(\"net.fortuna.ical4j.timezone.cache.impl\", MapTimeZoneCache.class.getName());\n\n        CalendarView calendarView = new CalendarView();\n        calendarView.setToday(LocalDate.now());\n        calendarView.setTime(LocalTime.now());\n        calendarView.addEventFilter(LoadEvent.LOAD, evt -> {\n\n            /*\n             * Run in background thread. We do not want to block the UI.\n             */\n            executor.submit(() -> {\n                for (CalendarSource source : evt.getCalendarSources()) {\n                    for (Calendar calendar : source.getCalendars()) {\n                        if (calendar instanceof ICalCalendar) {\n                            ICalCalendar account = (ICalCalendar) calendar;\n                            account.load(evt);\n                        }\n                    }\n                }\n            });\n        });\n\n        Thread updateTimeThread = new Thread(\"Calendar: Update Time Thread\") {\n            @Override\n            public void run() {\n                while (true) {\n                    Platform.runLater(() -> {\n                        calendarView.setToday(LocalDate.now());\n                        calendarView.setTime(LocalTime.now());\n                    });\n\n                    try {\n                        // update every 10 seconds\n                        sleep(10000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        };\n\n        updateTimeThread.setPriority(Thread.MIN_PRIORITY);\n        updateTimeThread.setDaemon(true);\n        updateTimeThread.start();\n\n        calendarView.setRequestedTime(LocalTime.now());\n        calendarView.setTraysAnimated(false);\n        calendarView.setCalendarSourceFactory(new ICalWebSourceFactory(primaryStage));\n        calendarView.getCalendarSources().setAll(ICalRepository.familyCalendars, ICalRepository.communityCalendars);\n\n        Task<Void> task = new Task<>() {\n\n            @Override\n            protected Void call() throws Exception {\n                ICalRepository.workDoneProperty.addListener(it -> updateProgress(ICalRepository.workDoneProperty.get(), ICalRepository.totalWorkProperty.get()));\n                ICalRepository.totalWorkProperty.addListener(it -> updateProgress(ICalRepository.workDoneProperty.get(), ICalRepository.totalWorkProperty.get()));\n                ICalRepository.messageProperty.addListener(it -> updateMessage(ICalRepository.messageProperty.get()));\n                ICalRepository.loadSources();\n                return null;\n            }\n        };\n\n        ImageView logo = new ImageView(ICalCalendarApp.class.getResource(\"ical.png\").toExternalForm());\n        logo.setFitWidth(64);\n        logo.setPreserveRatio(true);\n\n        ProgressDialog progressDialog = new ProgressDialog(task);\n        progressDialog.setGraphic(logo);\n        progressDialog.initModality(Modality.NONE);\n        progressDialog.initStyle(StageStyle.UTILITY);\n        progressDialog.initOwner(primaryStage.getOwner());\n        progressDialog.setTitle(\"Progress\");\n        progressDialog.setHeaderText(\"Importing Online Calendars\");\n        progressDialog.setContentText(\"The application is now downloading several calendars from the web.\");\n        progressDialog.getDialogPane().setPrefWidth(500);\n        progressDialog.getDialogPane().getStylesheets().clear();\n        progressDialog.getDialogPane().getStylesheets().add(ICalCalendarApp.class.getResource(\"dialog.css\").toExternalForm());\n\n        executor.submit(task);\n\n        Scene scene = new Scene(calendarView);\n\n        primaryStage.setTitle(\"iCalendar\");\n        primaryStage.setScene(scene);\n        primaryStage.setWidth(1400);\n        primaryStage.setHeight(1000);\n        primaryStage.centerOnScreen();\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/ICalCalendarAppLauncher.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical;\n\npublic class ICalCalendarAppLauncher {\n\n    public static void main(String[] args) {\n        System.setProperty(\"calendarfx.developer\", \"true\");\n        ICalCalendarApp.main(args);\n    }\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/ICalRepository.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical;\n\nimport com.calendarfx.ical.model.ICalCalendar;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport javafx.application.Platform;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport net.fortuna.ical4j.data.CalendarBuilder;\nimport net.fortuna.ical4j.data.ParserException;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * Created by gdiaz on 7/01/2017.\n */\npublic final class ICalRepository {\n\n    private static final String SETTINGS_DIR = \"/.store/calendarfx/\";\n\n    private static final String SETTINGS_FILE = \"iCalCalendars\";\n\n    private static final Map<WebCalendarData, ICalCalendar> webCalendars = new LinkedHashMap<>();\n\n    public static CalendarSource familyCalendars = new CalendarSource(\"Family\");\n\n    public static CalendarSource communityCalendars = new CalendarSource(\"Others\");\n\n    public static DoubleProperty workDoneProperty = new SimpleDoubleProperty();\n\n    public static DoubleProperty totalWorkProperty = new SimpleDoubleProperty();\n\n    public static StringProperty messageProperty = new SimpleStringProperty();\n\n    public static void loadSources() throws IOException, ParserException {\n\n//        loadWebSource();\n\n        if (true) { //familyCalendars.getCalendars().isEmpty()) {\n            totalWorkProperty.set(10);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/nextspaceflight.com_l328q9n2alm03mdukb05504c44%40group.calendar.google.com/public/basic.ics\", \"Space Launches\", Calendar.Style.STYLE1, communityCalendars);\n            createWebCalendar(\"https://i.cal.to/ical/2/fcbayern/bundesliga-spielplan/1406373c.bca6824f-04acfda3.ics\", \"Bayern München\", Calendar.Style.STYLE2, communityCalendars);\n\n//            createWebCalendar(\"https://cantonbecker.com/astronomy-calendar/astrocal.ics\", \"Moon / Astro\", Calendar.Style.STYLE2, communityCalendars);\n//            workDoneProperty.set(2);\n//            createWebCalendar(\"http://ical.mac.com/ical/US32Holidays.ics\", \"US Holidays\", Calendar.Style.STYLE3, communityCalendars);\n//            workDoneProperty.set(3);\n//            createWebCalendar(\"https://www.google.com/calendar/ical/6g08e17mnjao5k7ddftfvq5gs8%40group.calendar.google.com/public/basic.ics\", \"FC Liverpool\", Calendar.Style.STYLE5, communityCalendars);\n//            workDoneProperty.set(4);\n//            createWebCalendar(\"https://www.google.com/calendar/ical/ohg8jr90apq8k0vili2fbs17to%40group.calendar.google.com/public/basic.ics\", \"Real Madrid\", Calendar.Style.STYLE4, communityCalendars);\n//            workDoneProperty.set(5);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/flexcalendarfxdemo%40gmail.com/private-43f02ea9664382e80e6fbe0a541511ee/basic.ics\", \"Standard\", Calendar.Style.STYLE1, familyCalendars);\n//            workDoneProperty.set(6);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/75bjnbr2qr5qgetav71tug2sec%40group.calendar.google.com/private-c6c6a59d97aa2806fe28cfbdb2e2957b/basic.ics\", \"Home\", Calendar.Style.STYLE2, familyCalendars);\n//            workDoneProperty.set(7);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/5rj1uvaobtosjqoqqkpdlj01gg%40group.calendar.google.com/private-4dc56992aed93526cbab07da6cd4b69b/basic.ics\", \"School\", Calendar.Style.STYLE3, familyCalendars);\n//            workDoneProperty.set(8);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/0itqq6d7pukf1tapbll3lbad5c%40group.calendar.google.com/private-7fb78a4b949cede8228d791faba9061e/basic.ics\", \"Sports\", Calendar.Style.STYLE4, familyCalendars);\n//            workDoneProperty.set(9);\n//            createWebCalendar(\"https://calendar.google.com/calendar/ical/u6em5saa8omkamh68bl7fikclo%40group.calendar.google.com/private-bce22b1e9b43b632c7edfad45d677b59/basic.ics\", \"Work\", Calendar.Style.STYLE5, familyCalendars);\n//            workDoneProperty.set(10);\n\n        }\n    }\n\n    public static void loadWebSource() {\n        try (FileInputStream fin = new FileInputStream(new File(System.getProperty(\"user.home\") + SETTINGS_DIR, SETTINGS_FILE)); ObjectInputStream ois = new ObjectInputStream(fin)) {\n\n            List<WebCalendarData> webCalendars = (List<WebCalendarData>) ois.readObject();\n\n            if (webCalendars != null) {\n                totalWorkProperty.set(webCalendars.size());\n                double progress = 0;\n                for (WebCalendarData data : webCalendars) {\n                    putWebCalendar(data, data.isFamily() ? familyCalendars : communityCalendars);\n                    progress++;\n                    workDoneProperty.set(progress);\n                }\n            }\n        } catch (FileNotFoundException ex) {\n            // we can ignore this, this will happen first time we start the app\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static CalendarSource getCommunityCalendarSource() {\n        return communityCalendars;\n    }\n\n    public static boolean existsWebCalendar(String url) {\n        return getWebCalendar(url) != null;\n    }\n\n    public static ICalCalendar getWebCalendar(String url) {\n        for (WebCalendarData webCalendar : webCalendars.keySet()) {\n            if (webCalendar.getUrl().equals(url)) {\n                return webCalendars.get(webCalendar);\n            }\n        }\n        return null;\n    }\n\n    public static void createWebCalendar(String url, String name, Calendar.Style style, CalendarSource source) {\n        if (url == null || url.isEmpty() || name == null || name.isEmpty() || style == null) {\n            return;\n        }\n\n        try {\n            WebCalendarData data = new WebCalendarData(url, name, style, source == familyCalendars);\n\n            putWebCalendar(data, source);\n\n            List<WebCalendarData> webCalendarDatas = new ArrayList<>();\n            webCalendarDatas.addAll(webCalendars.keySet());\n\n            if (!webCalendarDatas.isEmpty()) {\n                final File directory = new File(System.getProperty(\"user.home\") + SETTINGS_DIR);\n                boolean directoryExists = true;\n                if (!directory.exists()) {\n                    directoryExists = directory.mkdirs();\n                }\n\n                if (directoryExists) {\n                    final File file = new File(directory, SETTINGS_FILE);\n\n                    boolean fileExists = true;\n                    if (!file.exists()) {\n                        fileExists = file.createNewFile();\n                    }\n\n                    if (fileExists) {\n                        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {\n                            oos.writeObject(webCalendarDatas);\n                        } catch (Exception e) {\n                            e.printStackTrace();\n                        }\n                    }\n                }\n            }\n\n        } catch (Throwable t) {}\n    }\n\n    private static void putWebCalendar(WebCalendarData data, CalendarSource source) throws IOException, ParserException {\n        ICalCalendar cal = getWebCalendar(data.getUrl());\n        if (cal == null) {\n\n            messageProperty.set(\"Calendar: \" + data.getName());\n\n            URL urlObj = new URL(data.getUrl().replace(\"webcal\", \"https\"));\n\n            HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();\n            conn.setDoOutput(true);\n            conn.setConnectTimeout(5000);\n            conn.setReadTimeout(15000);\n\n            CalendarBuilder builder = new CalendarBuilder();\n            InputStream inputStream = conn.getInputStream();\n\n            net.fortuna.ical4j.model.Calendar calendar = builder.build(inputStream);\n\n            cal = new ICalCalendar(data.getName(), calendar);\n            cal.setStyle(data.getStyle());\n\n            webCalendars.put(data, cal);\n\n            final ICalCalendar fcal = cal;\n\n            Platform.runLater(() -> {\n                source.getCalendars().add(fcal);\n            });\n        }\n    }\n\n    private static class WebCalendarData implements Serializable {\n\n        private final String url;\n        private final String name;\n        private final Calendar.Style style;\n        private final boolean family;\n\n        public WebCalendarData(String url, String name, Calendar.Style style, boolean family) {\n            this.url = url;\n            this.name = name;\n            this.style = style;\n            this.family = family;\n        }\n\n        public boolean isFamily() {\n            return family;\n        }\n\n        public String getUrl() {\n            return url;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public Calendar.Style getStyle() {\n            return style;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n\n            WebCalendarData that = (WebCalendarData) o;\n\n            return Objects.equals(url, that.url);\n        }\n\n        @Override\n        public int hashCode() {\n            return url != null ? url.hashCode() : 0;\n        }\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/model/ICalCalendar.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical.model;\n\nimport com.calendarfx.model.Interval;\nimport com.calendarfx.model.LoadEvent;\nimport net.fortuna.ical4j.filter.Filter;\nimport net.fortuna.ical4j.filter.predicate.PeriodRule;\nimport net.fortuna.ical4j.model.Calendar;\nimport net.fortuna.ical4j.model.Component;\nimport net.fortuna.ical4j.model.Period;\nimport net.fortuna.ical4j.model.Property;\nimport net.fortuna.ical4j.model.component.VEvent;\nimport net.fortuna.ical4j.model.property.DtEnd;\nimport net.fortuna.ical4j.model.property.DtStart;\nimport net.fortuna.ical4j.model.property.RRule;\nimport net.fortuna.ical4j.model.property.Uid;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.Year;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.Temporal;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport static java.util.Objects.requireNonNull;\n\npublic class ICalCalendar extends com.calendarfx.model.Calendar {\n\n    private final Set<Integer> alreadyLoadedYears = new HashSet<>();\n\n    private final Calendar calendar;\n\n    private final Set<Uid> loadedEventIds = new HashSet<>();\n\n    public ICalCalendar(String name, Calendar calendar) {\n        super(name);\n\n        requireNonNull(calendar);\n\n        this.calendar = calendar;\n\n        load(Year.now().getValue());\n    }\n\n    public void load(LoadEvent event) {\n        load(event.getStartTime().getYear());\n    }\n\n    /*\n     * Use synchronization to ensure consistency of already loaded dates.\n     */\n    private synchronized void load(int year) {\n        if (alreadyLoadedYears.contains(year)) {\n            return;\n        }\n\n        alreadyLoadedYears.add(year);\n\n        ZonedDateTime st = ZonedDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfYear()), LocalTime.MIN, ZoneId.systemDefault());\n        ZonedDateTime et = ZonedDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfYear()), LocalTime.MAX, ZoneId.systemDefault());\n\n        try {\n            startBatchUpdates();\n\n            Period<Instant> period = new Period<>(st.toInstant(), et.toInstant());\n            Filter<VEvent> filter = new Filter<>(new PeriodRule<>(period));\n            Collection<VEvent> events = calendar.getComponents(Component.VEVENT);\n\n            for (VEvent evt : events) {\n\n                try {\n                    Optional<Uid> uid = evt.getProperty(Property.UID);\n                    if (!uid.isPresent()) {\n                        continue;\n                    }\n\n                    if (loadedEventIds.contains(uid.get())) {\n                        continue;\n                    }\n\n                    loadedEventIds.add(uid.get());\n\n                    ICalCalendarEntry entry = new ICalCalendarEntry(evt);\n\n                    Optional<DtStart> dtStartOptional = evt.getProperty(Property.DTSTART);\n                    Optional<DtEnd> dtEndOptional = evt.getProperty(Property.DTEND);\n\n                    if (!(dtStartOptional.isPresent() && dtEndOptional.isPresent())) {\n                        continue;\n                    }\n\n                    Temporal start = dtStartOptional.get().getDate();\n                    Temporal end = dtEndOptional.get().getDate();\n\n                    ZonedDateTime entryStart = null;\n                    ZonedDateTime entryEnd = null;\n\n                    if (start instanceof Instant) {\n                        entryStart = ZonedDateTime.ofInstant((Instant) start, ZoneId.systemDefault());\n                        entryEnd = ZonedDateTime.ofInstant((Instant) end, ZoneId.systemDefault());\n                    } else if (start instanceof LocalDate) {\n                        entryStart = ZonedDateTime.of((LocalDate) start, LocalTime.MIN, ZoneId.systemDefault());\n                        entryEnd = ZonedDateTime.of((LocalDate) end, LocalTime.MAX, ZoneId.systemDefault());\n                        entry.setFullDay(true);\n                    } else if (start instanceof ZonedDateTime) {\n                        entryStart = (ZonedDateTime) start;\n                        entryEnd = (ZonedDateTime) end;\n                        entryStart = entryStart.withZoneSameInstant(ZoneId.systemDefault());\n                        entryEnd = entryEnd.withZoneSameInstant(ZoneId.systemDefault());\n                    }\n\n                    if (entryStart == null || entryEnd == null) {\n                        continue;\n                    }\n\n                    entry.setInterval(new Interval(entryStart, entryEnd));\n\n                    final Optional<RRule> prop = evt.getProperty(Property.RRULE);\n                    if (prop.isPresent()) {\n                        RRule rrule = prop.get();\n                        entry.setRecurrenceRule(\"RRULE:\" + rrule.getValue());\n                    }\n\n                    addEntry(entry);\n                } catch (Throwable t) {\n                }\n            }\n        } catch (Throwable ex) {\n            ex.printStackTrace();\n        } finally {\n            stopBatchUpdates();\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/model/ICalCalendarEntry.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical.model;\n\nimport com.calendarfx.model.Entry;\nimport net.fortuna.ical4j.model.Property;\nimport net.fortuna.ical4j.model.component.VEvent;\nimport net.fortuna.ical4j.model.property.Location;\nimport net.fortuna.ical4j.model.property.Summary;\n\nimport java.util.Optional;\n\npublic class ICalCalendarEntry extends Entry<VEvent> {\n\n    public ICalCalendarEntry(VEvent event) {\n        Optional<Summary> summary = event.getProperty(Property.SUMMARY);\n        if (summary.isPresent()) {\n            setTitle(summary.get().getValue());\n        }\n        Optional<Location> optionalLocation = event.getProperty(Property.LOCATION);\n        if (optionalLocation.isPresent()) {\n            setLocation(optionalLocation.get().getValue());\n        }\n    }\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/view/ICalWebSourceFactory.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical.view;\n\nimport com.calendarfx.ical.ICalRepository;\nimport com.calendarfx.model.Calendar;\nimport com.calendarfx.model.CalendarSource;\nimport com.calendarfx.view.CalendarView;\nimport com.calendarfx.view.DateControl;\nimport javafx.event.ActionEvent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.Window;\nimport javafx.util.Callback;\n\n/**\n * Calendar Source Factory that shows up a dialog to enter a web based URL of an iCal.\n * <p>\n * Created by gdiaz on 5/01/2017.\n */\npublic final class ICalWebSourceFactory implements Callback<DateControl.CreateCalendarSourceParameter, CalendarSource> {\n\n    private final Window owner;\n    private ICalWebSourcePane pane;\n    private Stage dialog;\n\n    public ICalWebSourceFactory(Window owner) {\n        this.owner = owner;\n    }\n\n    @Override\n    public CalendarSource call(DateControl.CreateCalendarSourceParameter param) {\n        if (dialog == null) {\n            pane = new ICalWebSourcePane();\n            pane.setOnCancelClicked(this::cancel);\n            pane.setOnAcceptClicked(this::accept);\n            pane.getStylesheets().add(CalendarView.class.getResource(\"calendar.css\").toExternalForm());\n\n            dialog = new Stage();\n            dialog.initOwner(owner);\n            dialog.setScene(new Scene(pane));\n            dialog.sizeToScene();\n            dialog.centerOnScreen();\n            dialog.setTitle(\"Add Web iCal\");\n            dialog.initModality(Modality.APPLICATION_MODAL);\n        }\n\n        dialog.showAndWait();\n\n        return ICalRepository.getCommunityCalendarSource();\n    }\n\n    private void cancel(ActionEvent evt) {\n        pane.clear();\n        dialog.hide();\n    }\n\n    private void accept(ActionEvent evt) {\n        String url = pane.getUrl();\n        String name = pane.getName();\n        Calendar.Style style = pane.getCalendarStyle();\n\n        if (ICalRepository.existsWebCalendar(url)) {\n            Alert alert = new Alert(Alert.AlertType.WARNING);\n            alert.setHeaderText(\"This calendar was already added!\");\n            alert.show();\n            return;\n        }\n\n        ICalRepository.createWebCalendar(url, name, style, ICalRepository.getCommunityCalendarSource());\n        pane.clear();\n        dialog.hide();\n    }\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/com/calendarfx/ical/view/ICalWebSourcePane.java",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage com.calendarfx.ical.view;\n\nimport com.calendarfx.model.Calendar;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.Separator;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.shape.Rectangle;\n\n/**\n * Pane that allows to enter a web URL of an iCal.\n *\n * Created by gdiaz on 5/01/2017.\n */\npublic class ICalWebSourcePane extends BorderPane {\n\n    private final TextField urlField;\n\n    private final TextField nameField;\n\n    private final ComboBox<Calendar.Style> styleComboBox;\n\n    private final Button acceptButton;\n\n    private final Button cancelButton;\n\n    public ICalWebSourcePane() {\n        urlField = new TextField();\n        urlField.setPrefWidth(300);\n        nameField = new TextField();\n        styleComboBox = new ComboBox<>();\n        styleComboBox.getItems().setAll(Calendar.Style.values());\n        styleComboBox.setButtonCell(new StyleCell());\n        styleComboBox.setCellFactory(listView -> new StyleCell());\n\n        acceptButton = new Button(\"Accept\");\n        cancelButton = new Button(\"Cancel\");\n\n        GridPane gridPane = new GridPane();\n        gridPane.add(new Label(\"URL\"), 0, 0);\n        gridPane.add(urlField, 1, 0);\n        gridPane.add(new Label(\"Name\"), 0, 1);\n        gridPane.add(nameField, 1, 1);\n        gridPane.add(new Label(\"Color\"), 0, 2);\n        gridPane.add(styleComboBox, 1, 2);\n        gridPane.getStyleClass().add(\"center\");\n        gridPane.setVgap(5);\n        gridPane.setHgap(5);\n        gridPane.setPadding(new Insets(10));\n\n        GridPane.setHgrow(urlField, Priority.ALWAYS);\n        GridPane.setHgrow(nameField, Priority.ALWAYS);\n        GridPane.setHgrow(styleComboBox, Priority.ALWAYS);\n\n        ButtonBar buttonBar = new ButtonBar();\n        buttonBar.getButtons().addAll(acceptButton, cancelButton);\n\n        VBox bottomPane = new VBox();\n        bottomPane.getChildren().addAll(new Separator(), buttonBar);\n        bottomPane.getStyleClass().add(\"bottom\");\n        bottomPane.setFillWidth(true);\n        bottomPane.setSpacing(10);\n\n        setCenter(gridPane);\n        setBottom(bottomPane);\n        getStyleClass().add(\"ical-web-source-pane\");\n        setPadding(new Insets(15));\n    }\n\n    public final void setOnCancelClicked(EventHandler<ActionEvent> onCancelClicked) {\n        cancelButton.setOnAction(onCancelClicked);\n    }\n\n    public final void setOnAcceptClicked(EventHandler<ActionEvent> onAcceptClicked) {\n        acceptButton.setOnAction(onAcceptClicked);\n    }\n\n    public final String getUrl() {\n        return urlField.getText();\n    }\n\n    public final String getName() {\n        return nameField.getText();\n    }\n\n    public final Calendar.Style getCalendarStyle() {\n        return styleComboBox.getValue();\n    }\n\n    public final void clear() {\n        urlField.setText(null);\n        nameField.setText(null);\n        styleComboBox.setValue(null);\n    }\n\n    private static class StyleCell extends ListCell<Calendar.Style> {\n        @Override\n        protected void updateItem(Calendar.Style item, boolean empty) {\n            super.updateItem(item, empty);\n            if (item != null && !empty) {\n                Rectangle icon = new Rectangle(12, 12);\n                icon.getStyleClass().add(item.name().toLowerCase() + \"-icon\");\n                setGraphic(icon);\n                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "CalendarFXiCal/src/main/java/module-info.java",
    "content": "module com.calendarfx.ical {\n    requires transitive ical4j.core;\n    requires transitive com.calendarfx.view;\n\n    requires java.logging;\n\n    exports com.calendarfx.ical;\n    exports com.calendarfx.ical.model;\n    exports com.calendarfx.ical.view;\n}"
  },
  {
    "path": "CalendarFXiCal/src/main/resources/com/calendarfx/ical/dialog.css",
    "content": "/*\n *  Copyright (C) 2017 Dirk Lemmermann Software & Consulting (dlsc.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *          http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n.progress-dialog.dialog-pane {\n    -fx-graphic: url(\"ical.png\");\n}"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# CalendarFX\nA Java framework for creating sophisticated calendar views based on JavaFX. A detailed developer manual can be found online: [CalendarFX Developer Manual](https://dlsc-software-consulting-gmbh.github.io/CalendarFX/)\n\n[![JFXCentral](https://img.shields.io/badge/Find_me_on-JFXCentral-blue?logo=googlechrome&logoColor=white)](https://www.jfx-central.com/libraries/calendarfx)\n\n[![Apache-2 license](https://img.shields.io/badge/license-Apache--2-%230778B9.svg)](https://opensource.org/licenses/Apache-2.0) \n[![Maven Central](https://img.shields.io/maven-central/v/com.calendarfx/view)](https://search.maven.org/search?q=g:com.calendarfx+AND+a:view) \n[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/dlsc-software-consulting-gmbh/CalendarFX)](https://lgtm.com/projects/g/dlsc-software-consulting-gmbh/CalendarFX/alerts)\n[![LGTM Grade](https://img.shields.io/lgtm/grade/java/github/dlsc-software-consulting-gmbh/CalendarFX)](https://lgtm.com/projects/g/dlsc-software-consulting-gmbh/CalendarFX/context:java)\n\n[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=dlsc-software-consulting-gmbh_CalendarFX2&metric=code_smells)](https://sonarcloud.io/dashboard?id=dlsc-software-consulting-gmbh_CalendarFX2.fx)\n[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=dlsc-software-consulting-gmbh_CalendarFX2&metric=ncloc)](https://sonarcloud.io/dashboard?id=dlsc-software-consulting-gmbh_CalendarFX2.fx)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=dlsc-software-consulting-gmbh_CalendarFX2&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=dlsc-software-consulting-gmbh_CalendarFX2.fx)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=dlsc-software-consulting-gmbh_CalendarFX2&metric=security_rating)](https://sonarcloud.io/dashboard?id=dlsc-software-consulting-gmbh_CalendarFX2.fx)\n[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=dlsc-software-consulting-gmbh_CalendarFX2&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=dlsc-software-consulting-gmbh_CalendarFX2.fx)\n\nFor a quick online demo please checkout [JPro](https://jpro.one) and their [CalendarFX demo](https://demos.jpro.one/calendar.html).\n\n![Screenshot](screenshot.png \"Screenshot\")\n\n# Repository Coordinates\nCalendarFX can be found on [The Central Repository](https://search.maven.org/search?q=g:com.calendarfx+AND+a:view) as `com.calendarfx:view`.\n\n# Modules\n\n* CalendarFXView — the main module containing the various calendar views\n* CalendarFXSampler — a demo app based on FXSampler to test controls individually\n* CalendarFXApp — a demo app (day, week, month, year views).\n* CalendarFXiCal — a demo app for working with iCalendar data\n* CalendarFXGoogle — a demo app for working with Google calendars\n* CalendarFXResourceApp — a demo app for the resource calendar view\n* CalendarFXWeather — a demo app for the month sheet view\n\n# Running\nIn the module folder of the corresponding app:\n```bash\nmvn javafx:run\n```\n\n# Building\nTo install the package into the local repository, for use as a dependency in other projects locally:\n```bash\nmvn install\n```\n\n# AtlantaFX\n\nTo use the AtlantaFX theming support you need to pass a system property to your application like this:\n\n```\n-Datlantafx=true\n```\n\nOr inside your application call:\n\n```\nSystem.setProperty(\"atlantafx\", \"true\");\n```\n\nDoing so will make the controls inside CalendarFX to always use the atlantafx.css file instead of the default calendar.css file.\n\nThe last step is to set one of the AtlantaFX themes as the default theme for your application. For example:\n\n```\nApplication.setUserAgentStylesheet(new NordDark().getUserAgentStylesheet());\n```\n\nObviously all of this requires that you have the AtlantaFX library on your classpath.\n\n\n\n\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<meta name=\"generator\" content=\"Asciidoctor 2.0.17\">\n<meta name=\"author\" content=\"Dirk Lemmermann\">\n<title>CalendarFX Developer Manual</title>\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700\">\n<style>\n/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */\n/* Uncomment the following line when using as a custom stylesheet */\n/* @import \"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700\"; */\nhtml{font-family:sans-serif;-webkit-text-size-adjust:100%}\na{background:none}\na:focus{outline:thin dotted}\na:active,a:hover{outline:0}\nh1{font-size:2em;margin:.67em 0}\nb,strong{font-weight:bold}\nabbr{font-size:.9em}\nabbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}\ndfn{font-style:italic}\nhr{height:0}\nmark{background:#ff0;color:#000}\ncode,kbd,pre,samp{font-family:monospace;font-size:1em}\npre{white-space:pre-wrap}\nq{quotes:\"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\"}\nsmall{font-size:80%}\nsub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}\nsup{top:-.5em}\nsub{bottom:-.25em}\nimg{border:0}\nsvg:not(:root){overflow:hidden}\nfigure{margin:0}\naudio,video{display:inline-block}\naudio:not([controls]){display:none;height:0}\nfieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}\nlegend{border:0;padding:0}\nbutton,input,select,textarea{font-family:inherit;font-size:100%;margin:0}\nbutton,input{line-height:normal}\nbutton,select{text-transform:none}\nbutton,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}\nbutton[disabled],html input[disabled]{cursor:default}\ninput[type=checkbox],input[type=radio]{padding:0}\nbutton::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\ntextarea{overflow:auto;vertical-align:top}\ntable{border-collapse:collapse;border-spacing:0}\n*,::before,::after{box-sizing:border-box}\nhtml,body{font-size:100%}\nbody{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}\na:hover{cursor:pointer}\nimg,object,embed{max-width:100%;height:auto}\nobject,embed{height:100%}\nimg{-ms-interpolation-mode:bicubic}\n.left{float:left!important}\n.right{float:right!important}\n.text-left{text-align:left!important}\n.text-right{text-align:right!important}\n.text-center{text-align:center!important}\n.text-justify{text-align:justify!important}\n.hide{display:none}\nimg,object,svg{display:inline-block;vertical-align:middle}\ntextarea{height:auto;min-height:50px}\nselect{width:100%}\n.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}\ndiv,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}\na{color:#2156a5;text-decoration:underline;line-height:inherit}\na:hover,a:focus{color:#1d4b8f}\na img{border:0}\np{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}\np aside{font-size:.875em;line-height:1.35;font-style:italic}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}\nh1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}\nh1{font-size:2.125em}\nh2{font-size:1.6875em}\nh3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}\nh4,h5{font-size:1.125em}\nh6{font-size:1em}\nhr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}\nem,i{font-style:italic;line-height:inherit}\nstrong,b{font-weight:bold;line-height:inherit}\nsmall{font-size:60%;line-height:inherit}\ncode{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;font-weight:400;color:rgba(0,0,0,.9)}\nul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}\nul,ol{margin-left:1.5em}\nul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}\nul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}\nul.square{list-style-type:square}\nul.circle{list-style-type:circle}\nul.disc{list-style-type:disc}\nol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}\ndl dt{margin-bottom:.3125em;font-weight:bold}\ndl dd{margin-bottom:1.25em}\nblockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}\nblockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}\n@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}\nh1{font-size:2.75em}\nh2{font-size:2.3125em}\nh3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}\nh4{font-size:1.4375em}}\ntable{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}\ntable thead,table tfoot{background:#f7f8f7}\ntable thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}\ntable tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}\ntable tr.even,table tr.alt{background:#f8f8f7}\ntable thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}\nh1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}\n.center{margin-left:auto;margin-right:auto}\n.stretch{width:100%}\n.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:\" \";display:table}\n.clearfix::after,.float-group::after{clear:both}\n:not(pre).nobreak{word-wrap:normal}\n:not(pre).nowrap{white-space:nowrap}\n:not(pre).pre-wrap{white-space:pre-wrap}\n:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}\npre{color:rgba(0,0,0,.9);font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;line-height:1.45;text-rendering:optimizeSpeed}\npre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}\npre>code{display:block}\npre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}\nem em{font-style:normal}\nstrong strong{font-weight:400}\n.keyseq{color:rgba(51,51,51,.8)}\nkbd{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}\n.keyseq kbd:first-child{margin-left:0}\n.keyseq kbd:last-child{margin-right:0}\n.menuseq,.menuref{color:#000}\n.menuseq b:not(.caret),.menuref{font-weight:inherit}\n.menuseq{word-spacing:-.02em}\n.menuseq b.caret{font-size:1.25em;line-height:.8}\n.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}\nb.button::before,b.button::after{position:relative;top:-1px;font-weight:400}\nb.button::before{content:\"[\";padding:0 3px 0 2px}\nb.button::after{content:\"]\";padding:0 2px 0 3px}\np a>code:hover{color:rgba(0,0,0,.9)}\n#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}\n#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:\" \";display:table}\n#header::after,#content::after,#footnotes::after,#footer::after{clear:both}\n#content{margin-top:1.25em}\n#content::before{content:none}\n#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}\n#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}\n#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}\n#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}\n#header .details span:first-child{margin-left:-.125em}\n#header .details span.email a{color:rgba(0,0,0,.85)}\n#header .details br{display:none}\n#header .details br+span::before{content:\"\\00a0\\2013\\00a0\"}\n#header .details br+span.author::before{content:\"\\00a0\\22c5\\00a0\";color:rgba(0,0,0,.85)}\n#header .details br+span#revremark::before{content:\"\\00a0|\\00a0\"}\n#header #revnumber{text-transform:capitalize}\n#header #revnumber::after{content:\"\\00a0\"}\n#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}\n#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}\n#toc>ul{margin-left:.125em}\n#toc ul.sectlevel0>li>a{font-style:italic}\n#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}\n#toc ul{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;list-style-type:none}\n#toc li{line-height:1.3334;margin-top:.3334em}\n#toc a{text-decoration:none}\n#toc a:active{text-decoration:underline}\n#toctitle{color:#7a2518;font-size:1.2em}\n@media screen and (min-width:768px){#toctitle{font-size:1.375em}\nbody.toc2{padding-left:15em;padding-right:0}\n#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}\n#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}\n#toc.toc2>ul{font-size:.9em;margin-bottom:0}\n#toc.toc2 ul ul{margin-left:0;padding-left:1em}\n#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}\nbody.toc2.toc-right{padding-left:0;padding-right:15em}\nbody.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}\n@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}\n#toc.toc2{width:20em}\n#toc.toc2 #toctitle{font-size:1.375em}\n#toc.toc2>ul{font-size:.95em}\n#toc.toc2 ul ul{padding-left:1.25em}\nbody.toc2.toc-right{padding-left:0;padding-right:20em}}\n#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}\n#content #toc>:first-child{margin-top:0}\n#content #toc>:last-child{margin-bottom:0}\n#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}\n#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}\n#content{margin-bottom:.625em}\n.sect1{padding-bottom:.625em}\n@media screen and (min-width:768px){#content{margin-bottom:1.25em}\n.sect1{padding-bottom:1.25em}}\n.sect1:last-child{padding-bottom:0}\n.sect1+.sect1{border-top:1px solid #e7e7e9}\n#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}\n#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:\"\\00A7\";font-size:.85em;display:block;padding-top:.1em}\n#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}\n#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}\n#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}\ndetails,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}\ndetails{margin-left:1.25rem}\ndetails>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}\ndetails>summary::-webkit-details-marker{display:none}\ndetails>summary::before{content:\"\";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}\ndetails[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}\ndetails>summary::after{content:\"\";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}\n.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;font-size:1rem;font-style:italic}\ntable.tableblock.fit-content>caption.title{white-space:nowrap;width:0}\n.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}\n.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}\n.admonitionblock>table td.icon{text-align:center;width:80px}\n.admonitionblock>table td.icon img{max-width:none}\n.admonitionblock>table td.icon .title{font-weight:bold;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;text-transform:uppercase}\n.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}\n.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}\n.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}\n.exampleblock>.content>:first-child{margin-top:0}\n.exampleblock>.content>:last-child{margin-bottom:0}\n.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}\n.sidebarblock>:first-child{margin-top:0}\n.sidebarblock>:last-child{margin-bottom:0}\n.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}\n.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}\n.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}\n@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}\n@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}\n.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^=\"highlight \"]{background:#f7f7f8}\n.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}\n.listingblock>.content{position:relative}\n.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}\n.listingblock:hover code[data-lang]::before{display:block}\n.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}\n.listingblock.terminal pre .command:not([data-prompt])::before{content:\"$\"}\n.listingblock pre.highlightjs{padding:0}\n.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}\n.listingblock pre.prettyprint{border-width:0}\n.prettyprint{background:#f7f7f8}\npre.prettyprint .linenums{line-height:1.45;margin-left:2em}\npre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}\npre.prettyprint li code[data-lang]::before{opacity:1}\npre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}\ntable.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}\ntable.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}\ntable.linenotable td.code{padding-left:.75em}\ntable.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}\npre.pygments span.linenos{display:inline-block;margin-right:.75em}\n.quoteblock{margin:0 1em 1.25em 1.5em;display:table}\n.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}\n.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}\n.quoteblock blockquote{margin:0;padding:0;border:0}\n.quoteblock blockquote::before{content:\"\\201c\";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}\n.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}\n.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}\n.verseblock{margin:0 1em 1.25em}\n.verseblock pre{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}\n.verseblock pre strong{font-weight:400}\n.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}\n.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}\n.quoteblock .attribution br,.verseblock .attribution br{display:none}\n.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}\n.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}\n.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}\n.quoteblock.abstract{margin:0 1em 1.25em;display:block}\n.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}\n.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}\n.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}\n.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}\n.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}\np.tableblock:last-child{margin-bottom:0}\ntd.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}\ntd.tableblock>.content>:last-child{margin-bottom:-1.25em}\ntable.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}\ntable.grid-all>*>tr>*{border-width:1px}\ntable.grid-cols>*>tr>*{border-width:0 1px}\ntable.grid-rows>*>tr>*{border-width:1px 0}\ntable.frame-all{border-width:1px}\ntable.frame-ends{border-width:1px 0}\ntable.frame-sides{border-width:0 1px}\ntable.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}\ntable.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}\ntable.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}\ntable.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}\ntable.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}\nth.halign-left,td.halign-left{text-align:left}\nth.halign-right,td.halign-right{text-align:right}\nth.halign-center,td.halign-center{text-align:center}\nth.valign-top,td.valign-top{vertical-align:top}\nth.valign-bottom,td.valign-bottom{vertical-align:bottom}\nth.valign-middle,td.valign-middle{vertical-align:middle}\ntable thead th,table tfoot th{font-weight:bold}\ntbody tr th{background:#f7f8f7}\ntbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}\np.tableblock>code:only-child{background:none;padding:0}\np.tableblock{font-size:1em}\nol{margin-left:1.75em}\nul li ol{margin-left:1.5em}\ndl dd{margin-left:1.125em}\ndl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}\nli p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}\nul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}\nul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}\nul.unstyled,ol.unstyled{margin-left:0}\nli>p:empty:only-child::before{content:\"\";display:inline-block}\nul.checklist>li>p:first-child{margin-left:-1em}\nul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}\nul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}\nul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}\nul.inline>li{margin-left:1.25em}\n.unstyled dl dt{font-weight:400;font-style:normal}\nol.arabic{list-style-type:decimal}\nol.decimal{list-style-type:decimal-leading-zero}\nol.loweralpha{list-style-type:lower-alpha}\nol.upperalpha{list-style-type:upper-alpha}\nol.lowerroman{list-style-type:lower-roman}\nol.upperroman{list-style-type:upper-roman}\nol.lowergreek{list-style-type:lower-greek}\n.hdlist>table,.colist>table{border:0;background:none}\n.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}\ntd.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}\ntd.hdlist1{font-weight:bold;padding-bottom:1.25em}\ntd.hdlist2{word-wrap:anywhere}\n.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}\n.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}\n.colist td:not([class]):first-child img{max-width:none}\n.colist td:not([class]):last-child{padding:.25em 0}\n.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}\n.imageblock.left{margin:.25em .625em 1.25em 0}\n.imageblock.right{margin:.25em 0 1.25em .625em}\n.imageblock>.title{margin-bottom:0}\n.imageblock.thumb,.imageblock.th{border-width:6px}\n.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}\n.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}\n.image.left{margin-right:.625em}\n.image.right{margin-left:.625em}\na.image{text-decoration:none;display:inline-block}\na.image object{pointer-events:none}\nsup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}\nsup.footnote a,sup.footnoteref a{text-decoration:none}\nsup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}\n#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}\n#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}\n#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}\n#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}\n#footnotes .footnote:last-of-type{margin-bottom:0}\n#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}\ndiv.unbreakable{page-break-inside:avoid}\n.big{font-size:larger}\n.small{font-size:smaller}\n.underline{text-decoration:underline}\n.overline{text-decoration:overline}\n.line-through{text-decoration:line-through}\n.aqua{color:#00bfbf}\n.aqua-background{background:#00fafa}\n.black{color:#000}\n.black-background{background:#000}\n.blue{color:#0000bf}\n.blue-background{background:#0000fa}\n.fuchsia{color:#bf00bf}\n.fuchsia-background{background:#fa00fa}\n.gray{color:#606060}\n.gray-background{background:#7d7d7d}\n.green{color:#006000}\n.green-background{background:#007d00}\n.lime{color:#00bf00}\n.lime-background{background:#00fa00}\n.maroon{color:#600000}\n.maroon-background{background:#7d0000}\n.navy{color:#000060}\n.navy-background{background:#00007d}\n.olive{color:#606000}\n.olive-background{background:#7d7d00}\n.purple{color:#600060}\n.purple-background{background:#7d007d}\n.red{color:#bf0000}\n.red-background{background:#fa0000}\n.silver{color:#909090}\n.silver-background{background:#bcbcbc}\n.teal{color:#006060}\n.teal-background{background:#007d7d}\n.white{color:#bfbfbf}\n.white-background{background:#fafafa}\n.yellow{color:#bfbf00}\n.yellow-background{background:#fafa00}\nspan.icon>.fa{cursor:default}\na span.icon>.fa{cursor:inherit}\n.admonitionblock td.icon [class^=\"fa icon-\"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}\n.admonitionblock td.icon .icon-note::before{content:\"\\f05a\";color:#19407c}\n.admonitionblock td.icon .icon-tip::before{content:\"\\f0eb\";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}\n.admonitionblock td.icon .icon-warning::before{content:\"\\f071\";color:#bf6900}\n.admonitionblock td.icon .icon-caution::before{content:\"\\f06d\";color:#bf3400}\n.admonitionblock td.icon .icon-important::before{content:\"\\f06a\";color:#bf0000}\n.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-style:normal;font-weight:bold}\n.conum[data-value] *{color:#fff!important}\n.conum[data-value]+b{display:none}\n.conum[data-value]::after{content:attr(data-value)}\npre .conum[data-value]{position:relative;top:-.125em}\nb.conum *{color:inherit!important}\n.conum:not([data-value]):empty{display:none}\ndt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}\nh1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}\np strong,td.content strong,div.footnote strong{letter-spacing:-.005em}\np,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}\np{margin-bottom:1.25rem}\n.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}\n.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}\n.print-only{display:none!important}\n@page{margin:1.25cm .75cm}\n@media print{*{box-shadow:none!important;text-shadow:none!important}\nhtml{font-size:80%}\na{color:inherit!important;text-decoration:underline!important}\na.bare,a[href^=\"#\"],a[href^=\"mailto:\"]{text-decoration:none!important}\na[href^=\"http:\"]:not(.bare)::after,a[href^=\"https:\"]:not(.bare)::after{content:\"(\" attr(href) \")\";display:inline-block;font-size:.875em;padding-left:.25em}\nabbr[title]{border-bottom:1px dotted}\nabbr[title]::after{content:\" (\" attr(title) \")\"}\npre,blockquote,tr,img,object,svg{page-break-inside:avoid}\nthead{display:table-header-group}\nsvg{max-width:100%}\np,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}\nh2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}\n#header,#content,#footnotes,#footer{max-width:none}\n#toc,.sidebarblock,.exampleblock>.content{background:none!important}\n#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}\nbody.book #header{text-align:center}\nbody.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}\nbody.book #header .details{border:0!important;display:block;padding:0!important}\nbody.book #header .details span:first-child{margin-left:0!important}\nbody.book #header .details br{display:block}\nbody.book #header .details br+span::before{content:none!important}\nbody.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}\nbody.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}\n.listingblock code[data-lang]::before{display:block}\n#footer{padding:0 .9375em}\n.hide-on-print{display:none!important}\n.print-only{display:block!important}\n.hide-for-print{display:none!important}\n.show-for-print{display:inherit!important}}\n@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}\n.sect1{padding:0!important}\n.sect1+.sect1{border:0}\n#footer{background:none}\n#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}\n@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}\n</style>\n<style>\n/*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */\npre.CodeRay{background:#f7f7f8}\n.CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}\n.CodeRay span.line-numbers{display:inline-block;margin-right:.75em}\n.CodeRay .line-numbers strong{color:#000}\ntable.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}\ntable.CodeRay td{vertical-align:top;line-height:inherit}\ntable.CodeRay td.line-numbers{text-align:right}\ntable.CodeRay td.code{padding:0 0 0 .75em}\n.CodeRay .debug{color:#fff!important;background:navy!important}\n.CodeRay .annotation{color:#007}\n.CodeRay .attribute-name{color:navy}\n.CodeRay .attribute-value{color:#700}\n.CodeRay .binary{color:#509}\n.CodeRay .comment{color:#998;font-style:italic}\n.CodeRay .char{color:#04d}\n.CodeRay .char .content{color:#04d}\n.CodeRay .char .delimiter{color:#039}\n.CodeRay .class{color:#458;font-weight:bold}\n.CodeRay .complex{color:#a08}\n.CodeRay .constant,.CodeRay .predefined-constant{color:teal}\n.CodeRay .color{color:#099}\n.CodeRay .class-variable{color:#369}\n.CodeRay .decorator{color:#b0b}\n.CodeRay .definition{color:#099}\n.CodeRay .delimiter{color:#000}\n.CodeRay .doc{color:#970}\n.CodeRay .doctype{color:#34b}\n.CodeRay .doc-string{color:#d42}\n.CodeRay .escape{color:#666}\n.CodeRay .entity{color:#800}\n.CodeRay .error{color:#808}\n.CodeRay .exception{color:inherit}\n.CodeRay .filename{color:#099}\n.CodeRay .function{color:#900;font-weight:bold}\n.CodeRay .global-variable{color:teal}\n.CodeRay .hex{color:#058}\n.CodeRay .integer,.CodeRay .float{color:#099}\n.CodeRay .include{color:#555}\n.CodeRay .inline{color:#000}\n.CodeRay .inline .inline{background:#ccc}\n.CodeRay .inline .inline .inline{background:#bbb}\n.CodeRay .inline .inline-delimiter{color:#d14}\n.CodeRay .inline-delimiter{color:#d14}\n.CodeRay .important{color:#555;font-weight:bold}\n.CodeRay .interpreted{color:#b2b}\n.CodeRay .instance-variable{color:teal}\n.CodeRay .label{color:#970}\n.CodeRay .local-variable{color:#963}\n.CodeRay .octal{color:#40e}\n.CodeRay .predefined{color:#369}\n.CodeRay .preprocessor{color:#579}\n.CodeRay .pseudo-class{color:#555}\n.CodeRay .directive{font-weight:bold}\n.CodeRay .type{font-weight:bold}\n.CodeRay .predefined-type{color:inherit}\n.CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold}\n.CodeRay .key{color:#808}\n.CodeRay .key .delimiter{color:#606}\n.CodeRay .key .char{color:#80f}\n.CodeRay .value{color:#088}\n.CodeRay .regexp .delimiter{color:#808}\n.CodeRay .regexp .content{color:#808}\n.CodeRay .regexp .modifier{color:#808}\n.CodeRay .regexp .char{color:#d14}\n.CodeRay .regexp .function{color:#404;font-weight:bold}\n.CodeRay .string{color:#d20}\n.CodeRay .string .string .string{background:#ffd0d0}\n.CodeRay .string .content{color:#d14}\n.CodeRay .string .char{color:#d14}\n.CodeRay .string .delimiter{color:#d14}\n.CodeRay .shell{color:#d14}\n.CodeRay .shell .delimiter{color:#d14}\n.CodeRay .symbol{color:#990073}\n.CodeRay .symbol .content{color:#a60}\n.CodeRay .symbol .delimiter{color:#630}\n.CodeRay .tag{color:teal}\n.CodeRay .tag-special{color:#d70}\n.CodeRay .variable{color:#036}\n.CodeRay .insert{background:#afa}\n.CodeRay .delete{background:#faa}\n.CodeRay .change{color:#aaf;background:#007}\n.CodeRay .head{color:#f8f;background:#505}\n.CodeRay .insert .insert{color:#080}\n.CodeRay .delete .delete{color:#800}\n.CodeRay .change .change{color:#66f}\n.CodeRay .head .head{color:#f4f}\n</style>\n</head>\n<body class=\"article toc2 toc-left\">\n<div id=\"header\">\n<h1>CalendarFX Developer Manual</h1>\n<div class=\"details\">\n<span id=\"author\" class=\"author\">Dirk Lemmermann</span><br>\n<span id=\"email\" class=\"email\"><a href=\"mailto:dlemmermann@gmail.com\">dlemmermann@gmail.com</a></span><br>\n</div>\n<div id=\"toc\" class=\"toc2\">\n<div id=\"toctitle\">Table of Contents</div>\n<ul class=\"sectlevel1\">\n<li><a href=\"#_quick_start\">Quick Start</a></li>\n<li><a href=\"#_model\">Model</a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_entry\">Entry</a></li>\n<li><a href=\"#_calendar\">Calendar</a></li>\n<li><a href=\"#_calendar_source\">Calendar Source</a></li>\n<li><a href=\"#_resource\">Resource</a></li>\n</ul>\n</li>\n<li><a href=\"#_events\">Events</a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_calendar_events\">Calendar Events</a></li>\n<li><a href=\"#_load_events\">Load Events</a></li>\n<li><a href=\"#_request_events\">Request Events</a></li>\n</ul>\n</li>\n<li><a href=\"#_datecontrol\">DateControl</a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_class_hierarchy\">Class Hierarchy</a></li>\n<li><a href=\"#_current_date_time_and_today\">Current Date, Time, and Today</a></li>\n<li><a href=\"#_adding_calendars_sources\">Adding Calendars / Sources</a></li>\n<li><a href=\"#_customizing_or_replacing_the_popover\">Customizing or Replacing the PopOver</a></li>\n<li><a href=\"#_context_menu_support\">Context Menu Support</a></li>\n<li><a href=\"#_creating_entries\">Creating Entries</a></li>\n<li><a href=\"#_creating_calendar_sources\">Creating Calendar Sources</a></li>\n</ul>\n</li>\n<li><a href=\"#_entry_views\">Entry Views</a></li>\n<li><a href=\"#_standard_calendar_views\">Standard Calendar Views</a></li>\n<li><a href=\"#_calendar_pages\">Calendar Pages</a></li>\n<li><a href=\"#_resource_scheduling_views\">Resource Scheduling Views</a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_resourcesview\">ResourcesView</a></li>\n</ul>\n</li>\n<li><a href=\"#_developer_console\">Developer Console</a></li>\n<li><a href=\"#_logging\">Logging</a></li>\n<li><a href=\"#_internationalization_i18n\">Internationalization (i18n)</a></li>\n<li><a href=\"#_known_issues\">Known Issues</a></li>\n</ul>\n</div>\n</div>\n<div id=\"content\">\n<div id=\"preamble\">\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>This is the <em>CalendarFX</em> developer manual. It aims to contain all the information required to quickly get a calendar UI into your application. If you notice any mistakes or if you are missing vital information then please let us know.</p>\n</div>\n<div class=\"imageblock text-center\">\n<div class=\"content\">\n<img src=\"manual-images/title.png\" alt=\"Calendar View\">\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_quick_start\">Quick Start</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>The following section shows you how to quickly set up a JavaFX application that will show a complete calendar user interface. It includes a day view, a week view, a month view, a year view, an agenda view, a calendar selection view, and a search UI.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">CalendarApp.java</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n61\n62\n63\n64\n65\n66\n67\n68\n69\n70\n71\n72\n</pre></td>\n  <td class=\"code\"><pre><span class=\"keyword\">package</span> <span class=\"namespace\">com.calendarfx.app</span>;\n\n<span class=\"keyword\">import</span> <span class=\"include\">java.time.LocalDate</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">java.time.LocalTime</span>;\n\n<span class=\"keyword\">import</span> <span class=\"include\">com.calendarfx.model.Calendar</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">com.calendarfx.model.Calendar.Style</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">com.calendarfx.model.CalendarSource</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">com.calendarfx.view.CalendarView</span>;\n\n<span class=\"keyword\">import</span> <span class=\"include\">javafx.application.Application</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">javafx.application.Platform</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">javafx.scene.Scene</span>;\n<span class=\"keyword\">import</span> <span class=\"include\">javafx.stage.Stage</span>;\n\n<span class=\"directive\">public</span> <span class=\"type\">class</span> <span class=\"class\">CalendarApp</span> <span class=\"directive\">extends</span> Application {\n\n        <span class=\"annotation\">@Override</span>\n        <span class=\"directive\">public</span> <span class=\"type\">void</span> start(Stage primaryStage) <span class=\"directive\">throws</span> <span class=\"exception\">Exception</span> {\n\n            CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView(); // <b class=\"conum\">(1)</b>\n\n                <span class=\"predefined-type\">Calendar</span> birthdays = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Calendar</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Birthdays</span><span class=\"delimiter\">&quot;</span></span>); // <b class=\"conum\">(2)</b>\n                <span class=\"predefined-type\">Calendar</span> holidays = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Calendar</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Holidays</span><span class=\"delimiter\">&quot;</span></span>);\n\n                birthdays.setStyle(<span class=\"predefined-type\">Style</span>.STYLE1); // <b class=\"conum\">(3)</b>\n                holidays.setStyle(<span class=\"predefined-type\">Style</span>.STYLE2);\n\n                CalendarSource myCalendarSource = <span class=\"keyword\">new</span> CalendarSource(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">My Calendars</span><span class=\"delimiter\">&quot;</span></span>); // <b class=\"conum\">(4)</b>\n                myCalendarSource.getCalendars().addAll(birthdays, holidays);\n\n                calendarView.getCalendarSources().addAll(myCalendarSource); // <b class=\"conum\">(5)</b>\n\n                calendarView.setRequestedTime(LocalTime.now());\n\n                <span class=\"predefined-type\">Thread</span> updateTimeThread = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Thread</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Calendar: Update Time Thread</span><span class=\"delimiter\">&quot;</span></span>) {\n                        <span class=\"annotation\">@Override</span>\n                        <span class=\"directive\">public</span> <span class=\"type\">void</span> run() {\n                                <span class=\"keyword\">while</span> (<span class=\"predefined-constant\">true</span>) {\n                                        Platform.runLater(() -&gt; {\n                                                calendarView.setToday(LocalDate.now());\n                                                calendarView.setTime(LocalTime.now());\n                                        });\n\n                                        <span class=\"keyword\">try</span> {\n                                                <span class=\"comment\">// update every 10 seconds</span>\n                                                sleep(<span class=\"integer\">10000</span>);\n                                        } <span class=\"keyword\">catch</span> (<span class=\"exception\">InterruptedException</span> e) {\n                                                e.printStackTrace();\n                                        }\n\n                                }\n                        }\n                };\n\n                updateTimeThread.setPriority(<span class=\"predefined-type\">Thread</span>.MIN_PRIORITY);\n                updateTimeThread.setDaemon(<span class=\"predefined-constant\">true</span>);\n                updateTimeThread.start();\n\n                Scene scene = <span class=\"keyword\">new</span> Scene(calendarView);\n                primaryStage.setTitle(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Calendar</span><span class=\"delimiter\">&quot;</span></span>);\n                primaryStage.setScene(scene);\n                primaryStage.setWidth(<span class=\"integer\">1300</span>);\n                primaryStage.setHeight(<span class=\"integer\">1000</span>);\n                primaryStage.centerOnScreen();\n                primaryStage.show();\n        }\n\n        <span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"type\">void</span> main(<span class=\"predefined-type\">String</span><span class=\"type\">[]</span> args) {\n                launch(args);\n        }\n}</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"colist arabic\">\n<ol>\n<li>\n<p>Create the calendar view</p>\n</li>\n<li>\n<p>Create one or more calendars</p>\n</li>\n<li>\n<p>Set a style on each calendar (entries will use different colors)</p>\n</li>\n<li>\n<p>Create a calendar source (e.g. \"Google\") and add calendars to it</p>\n</li>\n<li>\n<p>Add calendars to the view</p>\n</li>\n</ol>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_model\">Model</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>The primary model classes in <em>CalendarFX</em> are <code>CalendarSource</code>, <code>Calendar</code> and <code>Entry</code>. A calendar source often represents a calendar account, for example an account with \"Google Calendar\" (<a href=\"http://calendar.google.com\" class=\"bare\">http://calendar.google.com</a>). A calendar source consists of a list of calendars and each calendar manages any number of entries. An entry represents an event with a start date / time, an end date / time, and a time zone.</p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_entry\">Entry</h3>\n<div class=\"paragraph\">\n<p>The <code>Entry</code> class encapsulates all information that is required to display an event or an appointment in any of the calendar views included in <em>CalendarFX</em>.</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/entry.png\" alt=\"Calendar Entry\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>The properties of an entry are:</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">ID</dt>\n<dd>\n<p>a unique identifier</p>\n</dd>\n<dt class=\"hdlist1\">Title</dt>\n<dd>\n<p>The title / name of the event or appointment (e.g. \"Dentist Appointment\")</p>\n</dd>\n<dt class=\"hdlist1\">Calendar</dt>\n<dd>\n<p>The calendar to which the entry belongs.</p>\n</dd>\n<dt class=\"hdlist1\">Hidden</dt>\n<dd>\n<p>A flag that can be used to explicitly / manually hide an entry.</p>\n</dd>\n<dt class=\"hdlist1\">Interval</dt>\n<dd>\n<p>A complex data type grouping together start date / time, end date / time, and a time zone.</p>\n</dd>\n<dt class=\"hdlist1\">Location</dt>\n<dd>\n<p>A free text description of a location, for example \"Manhatten, New York\". This information can be used by Geo services to return coordinates so that the UI can display a map if needed.</p>\n</dd>\n<dt class=\"hdlist1\">Full Day</dt>\n<dd>\n<p>A flag used to signal that the event is relevant for the entire day and that the start and end times are not relevant, for example a birthday or a holiday. Full day entries are displayed as shown below.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock thumb\">\n<div class=\"content\">\n<img src=\"manual-images/all-day-view.png\" alt=\"All Day View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">Minimum Duration</dt>\n<dd>\n<p>Ensures that the user can not create entries with a duration of less than, for example, 15 minutes.</p>\n</dd>\n<dt class=\"hdlist1\">User Object</dt>\n<dd>\n<p>An arbitrary object which might be responsible for the creation of the entry in the first place.</p>\n</dd>\n<dt class=\"hdlist1\">Recurrence Rule</dt>\n<dd>\n<p>A text representation of a recurrence pattern according to RFC 2445 (\"RRULE:FREQ=DAILY\")</p>\n</dd>\n</dl>\n</div>\n<div class=\"admonitionblock important\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Important</div>\n</td>\n<td class=\"content\">\n<div class=\"paragraph\">\n<p>This last property is very interesting. It allows the entry to express that it defines a recurrence. The entry can specify that it will be repeated over and over again following a given pattern. For example: \"every Monday, Tuesday and Wednesday of every week until December 31st\". If an entry is indeed a recurring entry then it produces one or more \"recurrences\". These recurrences are created by the framework by invoking the <code>Entry.createRecurrence()</code> method. The result of this method is another Entry that will be configured with the same values as the source entry.</p>\n</div>\n</td>\n</tr>\n</table>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">Recurrence</dt>\n<dd>\n<p>A flag that expresses whether the entry represents a recurrence or not.</p>\n</dd>\n<dt class=\"hdlist1\">Recurrence Source</dt>\n<dd>\n<p>A reference to the original source entry.</p>\n</dd>\n<dt class=\"hdlist1\">Recurrence ID</dt>\n<dd>\n<p>If an entry represents a recurrence of a source entry then this property will store an additional ID, normally the date where the recurrence occurs.</p>\n</dd>\n</dl>\n</div>\n<div class=\"paragraph\">\n<p>In addition to these properties several read-only properties are available for convenience.</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">Multi Day</dt>\n<dd>\n<p>Needed y to easily determine if an entry spans multiple days. This information is constantly needed in various places of the framework for display / layout purposes.</p>\n</dd>\n<dt class=\"hdlist1\">Start Date</dt>\n<dd>\n<p>The date when the event begins (e.g. 5/12/2015).</p>\n</dd>\n<dt class=\"hdlist1\">Start Time</dt>\n<dd>\n<p>The time of day when the event begins (e.g. 2:15pm).</p>\n</dd>\n<dt class=\"hdlist1\">End Date</dt>\n<dd>\n<p>The date when the event ends (e.g. 8/12/2015).</p>\n</dd>\n<dt class=\"hdlist1\">End Time</dt>\n<dd>\n<p>The time of day when the event ends (e.g. 6:45pm).</p>\n</dd>\n</dl>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_calendar\">Calendar</h3>\n<div class=\"paragraph\">\n<p>The \"Calendar\" class is used to store entries in a binary interval tree. This data structure is not exposed to the outside. Instead methods exist on Calendar to add, remove, and find entries.</p>\n</div>\n<div class=\"paragraph\">\n<p>The following is a description of the main properties of the Calendar class:</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">Name</dt>\n<dd>\n<p>The display name of the calendar, shown in several places within the UI.</p>\n</dd>\n<dt class=\"hdlist1\">Short Name</dt>\n<dd>\n<p>A short version of the calendar name. By default, it is set to be equal to the regular name, but if the application is using the swimlane layout then it might make sense to also define a short name due to limited space.</p>\n</dd>\n<dt class=\"hdlist1\">Read-Only</dt>\n<dd>\n<p>A flag for controlling whether entries can be added interactively in the UI or not. Setting this flag to false does not prevent the application itself to add entries.</p>\n</dd>\n<dt class=\"hdlist1\">Style</dt>\n<dd>\n<p>Basically a name prefix for looking up different styles from the CSS file (calendar.css): <em>\"style1-\"</em>, <em>\"style2-\"</em>. The <code>Calendar</code> class defines an enumerator called <code>Style</code> that can be used to easily set the value of this property with one of the predefined styles.</p>\n</dd>\n<dt class=\"hdlist1\">Look Ahead / Back Duration</dt>\n<dd>\n<p>Two properties of type <code>java.time.Duration</code> that are used in combination with the current system time in order to create a time interval. The calendar class uses this time interval inside its <code>findEntries(String searchTerm)</code> method.</p>\n</dd>\n</dl>\n</div>\n<div class=\"sect3\">\n<h4 id=\"_adding_and_removing_entries\">Adding and Removing Entries</h4>\n<div class=\"paragraph\">\n<p>To add an entry simply call the <code>addEntry()</code> method on calendar.\nExample:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = ...\nEntry&lt;<span class=\"predefined-type\">String</span>&gt; dentistAppointment = <span class=\"keyword\">new</span> Entry&lt;&gt;(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Dentist</span><span class=\"delimiter\">&quot;</span></span>);\ncalendar.addEntry(dentistAppointment);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>To remove an entry call the <code>removeEntry()</code> method on calendar.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = ...\nEntry&lt;<span class=\"predefined-type\">String</span>&gt; dentistAppointment = ...\ncalendar.removeEntry(dentistAppointment);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>Alternatively you can simply set the calendar directly on the entry.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = ...\nEntry&lt;<span class=\"predefined-type\">String</span>&gt; dentistAppointment = ...\ndentistAppointment.setCalendar(calendar);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>To remove the entry from its calendar simply set the calendar to <em>null</em>.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>Entry&lt;<span class=\"predefined-type\">String</span>&gt; dentistAppointment = ...\ndentistAppointment.setCalendar(<span class=\"predefined-constant\">null</span>);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect3\">\n<h4 id=\"_finding_entries_for_a_time_interval\">Finding Entries for a Time Interval</h4>\n<div class=\"paragraph\">\n<p>The calendar class provides a <code>findEntries()</code> method which receives a start date, an end date, and a time zone. The result of invoking this method is a map where the keys are the dates for which entries were found and the values are lists of entries on that day.</p>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\n<div class=\"paragraph\">\n<p>The result does not only contain entries that were previously added by calling the <code>addEntry()</code> method but also recurrence entries that were generated on-the-fly for those entries that define a recurrence rule.</p>\n</div>\n</td>\n</tr>\n</table>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = ...\nMap&lt;LocalDate, <span class=\"predefined-type\">List</span>&lt;Entry&lt;?&gt;&gt;&gt; result = calendar.findEntries(LocalDate startDate,\n            LocalDate endDate, ZoneId zoneId)</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect3\">\n<h4 id=\"_finding_entries_for_a_search_string\">Finding Entries for a Search String</h4>\n<div class=\"paragraph\">\n<p>The second <code>findEntries()</code> method accepts a search term as a parameter and is used to find entries that were previously added to the calendar and that match the term.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = ...\nList&lt;Entry&lt;?&gt;&gt; result = calendar.findEntries(<span class=\"predefined-type\">String</span> searchTerm)</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>To find actual matches the method invokes the <code>Entry.matches(String)</code> method on all entries that are found within the time interval defined by the current date, the look back duration, and the look ahead duration.</p>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_calendar_source\">Calendar Source</h3>\n<div class=\"paragraph\">\n<p>A calendar source is used for creating a group of calendars. A very typical scenario would be that a calendar source represents an online calendar account (e.g. Google calendar). Calendars can be added to a source by simply calling <code>mySource.getCalendars().add(myCalendar)</code>.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_resource\">Resource</h3>\n<div class=\"paragraph\">\n<p>An instance of <code>Resource</code> represents a business object that can be scheduled. Hence, the resource model class contains a calendar for scheduled entries / events / allocations and also a calendar that represents the resource&#8217;s availability. The availability can be edited interactively in the <code>DayView</code> by setting the <code>editAvailability</code> property to true. It is important to note that it is the application&#8217;s responsibility to make sense out of the created entries inside the availability calendar. While editing is in progress each newly created time interval will be directly added to the availability calendar.</p>\n</div>\n<div class=\"exampleblock\">\n<div class=\"content\">\n<div class=\"paragraph\">\n<p>The <code>Resource</code> class is currently only used by the <code>ResourcesView</code>.</p>\n</div>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_events\">Events</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><em>CalendarFX</em> utilizes the JavaFX event model to inform the application about changes made in a calendar, about user interaction that might require loading of new data, and about user interaction that might require showing different views.</p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_calendar_events\">Calendar Events</h3>\n<div class=\"paragraph\">\n<p>An event type that indicates that a change was made to the data is probably the most obvious type that anyone would expect from a UI framework. In <em>CalendarFX</em> this event type is called <code>CalendarEvent</code>.</p>\n</div>\n<div class=\"ulist\">\n<div class=\"title\">Calendar Event Type Hierarchy</div>\n<ul>\n<li>\n<p><code>ANY</code> : the super event type</p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><code>CALENDAR_CHANGED</code> : \"something\" inside the calendar changed, usually causing rebuild of views (example: calendar batch updates finished)</p>\n</li>\n<li>\n<p><code>ENTRY_CHANGED</code> : the super type for changes made to an entry</p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><code>ENTRY_CALENDAR_CHANGED</code> : the entry was assigned to a different calendar</p>\n</li>\n<li>\n<p><code>ENTRY_FULL_DAY_CHANGED</code> : the full day flag was changed (from true to false or vice versa)</p>\n</li>\n<li>\n<p><code>ENTRY_INTERVAL_CHANGED</code> : the time interval of the entry was changed (start date / time, end date / time)</p>\n</li>\n<li>\n<p><code>ENTRY_LOCATION_CHANGED</code> : the location of the entry has changed</p>\n</li>\n<li>\n<p><code>ENTRY_RECURRENCE_RULE_CHANGED</code> : the recurrence rule was modified</p>\n</li>\n<li>\n<p><code>ENTRY_TITLE_CHANGED</code> : the entry title has changed</p>\n</li>\n<li>\n<p><code>ENTRY_USER_OBJECT_CHANGED</code> : a new user object was set on the entry</p>\n</li>\n</ul>\n</div>\n</li>\n</ul>\n</div>\n</li>\n</ul>\n</div>\n<div class=\"paragraph\">\n<p>Listeners for this event type can be added to calendars by calling:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> calendar = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Calendar</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Demo</span><span class=\"delimiter\">&quot;</span></span>);\n<span class=\"predefined-type\">EventHandler</span>&lt;CalendarEvent&gt; handler = evt -&gt; foo(evt);\ncalendar.addEventHandler(handler);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_load_events\">Load Events</h3>\n<div class=\"paragraph\">\n<p>Load events are used by the framework to signal to the application that the UI requires data for a specific time interval. This\ncan be very useful for implementing a lazy loading strategy. If the user switches from one month to another then an event of this\ntype will be fired and the time bounds on this event will be the first and the last day of that month. The <code>LoadEvent</code> type only\nsupports a single event type called <code>LOAD</code>.</p>\n</div>\n<div class=\"paragraph\">\n<p>Listeners for this event type can be registered on any date control:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>DayView view = <span class=\"keyword\">new</span> DayView();\nview.addEventHandler(LoadEvent.LOAD, evt -&gt; foo(evt));</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_request_events\">Request Events</h3>\n<div class=\"paragraph\">\n<p>A unique event class is <code>RequestEvent</code>. It is used by the controls of the framework to signal to other framework controls that\nthe user wants to \"jump\" to another view. For example: the user clicks on the date shown for a day in the <code>MonthView</code> then the month view will fire a request event that informs the framework that the user wants to switch to the <code>DayView</code> to see more detail for that day.</p>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_datecontrol\">DateControl</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>A calendar user interface hardly ever consists of just a single control. They are composed of several views, some showing a single day or a week or\na month. In <em>CalendarFX</em> the <code>CalendarView</code> control consists of dedicated \"pages\" for a day, a week, a month, or a full year. Each one of these pages consists of one or more subtypes of DateControl. The following image shows a simplified view of the scene graph / the containment hierarchy.</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/hierarchy.png\" alt=\"Hierarchy View\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>To make all of these controls work together in harmony it is important that they share many properties. This is accomplished by JavaFX property binding. The class <code>DateControl</code> features a method called \"bind\" that ensures the dates and times shown by the controls are synchronized. But also that  many of the customization features (e.g. node factories) are shared.</p>\n</div>\n<div class=\"paragraph\">\n<p>The following listing shows the implementation of the <code>DateControl.bind()</code> method to give you an idea how much is bound within <em>CalendarFX</em>.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n</pre></td>\n  <td class=\"code\"><pre>    <span class=\"directive\">public</span> <span class=\"directive\">final</span> <span class=\"type\">void</span> bind(DateControl otherControl, <span class=\"type\">boolean</span> bindDate) {\n\n        <span class=\"comment\">// bind lists</span>\n        Bindings.bindContentBidirectional(otherControl.getCalendarSources(),\n                getCalendarSources());\n        Bindings.bindContentBidirectional(otherControl.getSelections(),\n                getSelections());\n        Bindings.bindContentBidirectional(otherControl.getWeekendDays(),\n                getWeekendDays());\n\n        <span class=\"comment\">// bind properties</span>\n        Bindings.bindBidirectional(otherControl.entryFactoryProperty(),\n                entryFactoryProperty());\n        Bindings.bindBidirectional(otherControl.defaultCalendarProviderProperty(),\n                defaultCalendarProviderProperty());\n        Bindings.bindBidirectional(otherControl.virtualGridProperty(),\n                virtualGridProperty());\n        Bindings.bindBidirectional(otherControl.draggedEntryProperty(),\n                draggedEntryProperty());\n        Bindings.bindBidirectional(otherControl.requestedTimeProperty(),\n                requestedTimeProperty());\n\n        Bindings.bindBidirectional(otherControl.selectionModeProperty(),\n                selectionModeProperty());\n        Bindings.bindBidirectional(otherControl.selectionModeProperty(),\n                selectionModeProperty());\n        Bindings.bindBidirectional(otherControl.weekFieldsProperty(),\n                weekFieldsProperty());\n        Bindings.bindBidirectional(otherControl.layoutProperty(),\n                layoutProperty());\n\n        <span class=\"keyword\">if</span> (bindDate) {\n            Bindings.bindBidirectional(otherControl.dateProperty(), dateProperty());\n        }\n\n        Bindings.bindBidirectional(otherControl.todayProperty(),\n                todayProperty());\n        Bindings.bindBidirectional(otherControl.zoneIdProperty(),\n                zoneIdProperty());\n\n        <span class=\"comment\">// edit callbacks</span>\n        Bindings.bindBidirectional(\n                otherControl.entryDetailsCallbackProperty(),\n                entryDetailsCallbackProperty());\n        Bindings.bindBidirectional(\n                otherControl.dateDetailsCallbackProperty(),\n                dateDetailsCallbackProperty());\n        Bindings.bindBidirectional(\n                otherControl.contextMenuCallbackProperty(),\n                contextMenuCallbackProperty());\n        Bindings.bindBidirectional(\n                otherControl.entryContextMenuCallbackProperty(),\n                entryContextMenuCallbackProperty());\n        Bindings.bindBidirectional(\n                otherControl.calendarSourceFactoryProperty(),\n                calendarSourceFactoryProperty());\n        Bindings.bindBidirectional(\n                otherControl.entryDetailsPopOverContentCallbackProperty(),\n                entryDetailsPopOverContentCallbackProperty());\n    }</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_class_hierarchy\">Class Hierarchy</h3>\n<div class=\"paragraph\">\n<p><em>CalendarFX</em> ships with many built-in views for displaying calendar information. All of these views inherit from <code>DateControl</code>. The class\nhierarchy can be seen in the following image:</p>\n</div>\n<div class=\"imageblock text-center\">\n<div class=\"content\">\n<img src=\"manual-images/datecontrol.png\" alt=\"Class Hierarchy\">\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_current_date_time_and_today\">Current Date, Time, and Today</h3>\n<div class=\"paragraph\">\n<p>Each <code>DateControl</code> keeps track of the \"current date\" and \"today\". The current date is the date that the control is supposed to display to the user.\n\"Today\" is the date that the control assumes to be the actual date. \"Today\" defaults to the current system date (provided by the operating system), but\nit can be any date.</p>\n</div>\n<div class=\"admonitionblock important\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Important</div>\n</td>\n<td class=\"content\">\n<div class=\"title\">Updating today and current time</div>\n<div class=\"paragraph\">\n<p>The \"today\" and \"time\" properties do not get updated by themselves. See the daemon thread created in the listing shown in the \"Quick Start\" section.</p>\n</div>\n</td>\n</tr>\n</table>\n</div>\n<div class=\"paragraph\">\n<p><code>DateControl</code> defines utility methods that allow for easy modification of the \"current\" date.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre><span class=\"directive\">public</span> <span class=\"type\">void</span> goToday();\n<span class=\"directive\">public</span> <span class=\"type\">void</span> goForward();\n<span class=\"directive\">public</span> <span class=\"type\">void</span> goBack();</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_adding_calendars_sources\">Adding Calendars / Sources</h3>\n<div class=\"paragraph\">\n<p>Even though the <code>DateControl</code> class provides a <code>getCalendars()</code> method this is not the place where calendars are being added. Instead,\nalways create calendar sources, add calendars to them, and then add the sources to the control. The \"calendars\" list is a read-only\nflat list representation of all calendars in all calendar sources. The \"calendars\" list gets updated by the framework.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">Adding Calendars</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n4\n5\n6\n7\n8\n</pre></td>\n  <td class=\"code\"><pre><span class=\"predefined-type\">Calendar</span> katja = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Calendar</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Katja</span><span class=\"delimiter\">&quot;</span></span>);\n<span class=\"predefined-type\">Calendar</span> dirk = <span class=\"keyword\">new</span> <span class=\"predefined-type\">Calendar</span>(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Dirk</span><span class=\"delimiter\">&quot;</span></span>);\n\nCalendarSource familyCalendarSource = <span class=\"keyword\">new</span> CalendarSource(<span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">Family</span><span class=\"delimiter\">&quot;</span></span>);\nfamilyCalendarSource.getCalendars().addAll(katja, dirk);\n\nCalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.getCalendarSources().setAll(familyCalendarSource);</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_customizing_or_replacing_the_popover\">Customizing or Replacing the PopOver</h3>\n<div class=\"paragraph\">\n<p>The <code>DateControl</code> class has built-in support for displaying a <code>PopOver</code> control when the user double-clicks on a calendar entry. The content node of this <code>PopOver</code> can be replaced. It is normally used to show some basic entry details (e.g. start / end date, title, event location) but applications might have defined specialized entries with custom properties that require additional UI elements. This can be accomplished by the help of the <code>PopOver</code> content node factory.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">PopOver Content Node Factory</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setEntryDetailsPopOverContentCallback(param -&gt; <span class=\"keyword\">new</span> MyCustomPopOverContentNode());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>If an application does not want to use the <code>PopOver</code> at all but instead display a standard dialog then there is a way of doing that, too. Simply\nregister an entry details callback.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">Entry Details Callback</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setEntryDetailsCallback(param -&gt; <span class=\"keyword\">new</span> MyCustomEntryDialog());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>These two callbacks normally work hand in hand. The default implementation of the entry details callback is producing a <code>PopOver</code> and sets the content\nnode on the PopOver via the help of the content node callback.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_context_menu_support\">Context Menu Support</h3>\n<div class=\"paragraph\">\n<p>A common place for customization are context menus. The <code>DateControl</code> class produces a context menu via specialized callbacks. One callback is used\nto produce a menu for a given calendar entry, the second callback is used when the user triggers the context menu by clicking in the background\nof a <code>DateControl</code>.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">PopOver Content Node Factory</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n3\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setEntryContextMenuCallback(param -&gt; <span class=\"keyword\">new</span> MyEntryContextMenu());\ncalendarView.setContextMenuCallback(param -&gt; <span class=\"keyword\">new</span> MyContextMenu());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"admonitionblock important\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Important</div>\n</td>\n<td class=\"content\">\n<div class=\"title\">Context Menus</div>\n<div class=\"paragraph\">\n<p>The context menu callbacks are automatically shared among all date controls that are bound to each other. The same context menu code will execute for\ndifferent views, the <code>DayView</code>, the <code>MonthView</code>, and so on. This means that the code that builds the context menu will need to check the parameter object\nthat was passed to the callback to configure itself appropriately.</p>\n</div>\n<div class=\"paragraph\">\n<p>The same is true for basically all callbacks used by the DateControl.</p>\n</div>\n</td>\n</tr>\n</table>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_creating_entries\">Creating Entries</h3>\n<div class=\"paragraph\">\n<p>The user can create new entries by double-clicking anywhere inside a <code>DateControl</code>. The actual work of creating a new entry instance is then delegated to a specialized entry factory that can be set on <code>DateControl</code>.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">Entry Factory</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setEntryFactory(param -&gt; <span class=\"keyword\">new</span> MyEntryFactory());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>Once the entry factory has returned the new entry it will be added to the calendar that is being returned by the \"default calendar\" provider. This provider is also customizable via a callback.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">Default Calendar Provider</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setDefaultCalendarProvider(param -&gt; <span class=\"keyword\">new</span> MyDefaultCalendarProvider());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>Besides the double click creation the application can also programmatically request the DateControl to create a new entry at a given point in time. Two methods are available for this: createEntryAt(ZonedDateTime) and createEntryAt(ZonedDateTime, Calendar). The second method will ensure that the entry will be added to the given calendar while the first method will invoke the default calendar provider.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_creating_calendar_sources\">Creating Calendar Sources</h3>\n<div class=\"paragraph\">\n<p>The user might also wish to add another calendar source to the application. In this case the DateControl will invoke the calendar source factory. The default implementation of this factory does nothing more than to create a new instance of the standard CalendarSource class. Applications are free to return a specialization of CalendarSource instead (e.g. GoogleCalendarAccount). A custom factory might even prompt the user first with a dialog, e.g. to request user credentials.</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"title\">Default Calendar Provider</div>\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code data-lang=\"java\"><table class=\"CodeRay\"><tr>\n  <td class=\"line-numbers\"><pre>1\n2\n</pre></td>\n  <td class=\"code\"><pre>CalendarView calendarView = <span class=\"keyword\">new</span> CalendarView();\ncalendarView.setCalendarSourceFactory(param -&gt; <span class=\"keyword\">new</span> MyCalendarSource());</pre></td>\n</tr></table></code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>The calendar source factory gets invoked when the method <code>DateControl.createCalendarSource()</code> gets invoked. The <code>CalendarView</code> class already provides a button\nin its toolbar that will call this method.</p>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_entry_views\">Entry Views</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Entry views are JavaFX nodes that are representing calendar entries. There are several types, all extending <code>EntryViewBase</code>:</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">Day Entry View</dt>\n<dd>\n<p>Shown inside a <code>DayView</code> or <code>WeekDayView</code> control. These views can be customized by subclassing <code>DayEntryViewSkin</code> and overriding the <code>createContent()</code> method.</p>\n</dd>\n<dt class=\"hdlist1\">All Day Entry View</dt>\n<dd>\n<p>Shown inside the <code>AllDayView</code> control.</p>\n</dd>\n<dt class=\"hdlist1\">Month Entry View</dt>\n<dd>\n<p>Shown inside the <code>MonthView</code> control.</p>\n</dd>\n</dl>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_standard_calendar_views\">Standard Calendar Views</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>The most fundamental views inside <em>CalendarFX</em> are of course the views used to display a day (24 hours), an entire week, a month, and a year.</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">DayView</dt>\n<dd>\n<p>Shows a 24-hour time period vertically. The control has several options that can be used to influence the layout of the hours. E.g.: it is possible to define hour ranges where the time will be compressed in order to save space on the screen (early and late hours are often not relevant). The view can also specify whether it wants to always show a fixed number of hours or a fixed height for each hour.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/day-view.png\" alt=\"Day View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">DetailedDayView</dt>\n<dd>\n<p>wraps the <code>DayView</code> control with several additional controls: an <code>AllDayView</code>, a <code>TimeScaleView</code>, a <code>CalendarHeaderView</code>, a <code>ScrollBar</code> and an (optional)\n<code>AgendaView</code>.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/detailed-day-view-agenda.png\" alt=\"Detailed Day View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">WeekView</dt>\n<dd>\n<p>The name of this control is somewhat misleading, because it can show any number of <code>WeekDayView</code> instances, not just 5 or 7 but also 14 (two weeks) or 21 (three weeks). In this view entries can be easily edited to span multiple days.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/week-view.png\" alt=\"Week View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">DetailedWeekView</dt>\n<dd>\n<p>same concept as the <code>DetailedDayView</code>. This view wraps the <code>WeekView</code> and adds several other controls.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/detailed-week-view.png\" alt=\"Detailed Week View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">MonthView</dt>\n<dd>\n<p>Shows up to 31 days for the current month plus some days of the previous and the next month.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/month-view.png\" alt=\"Month View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">MonthSheetView</dt>\n<dd>\n<p>Shows several months in a column layout. Weekdays can be aligned so that the same weekdays are always next to each other. A customizable\ncell factory is used to create the date cells. Several default implementations are included in <em>CalendarFX</em>: simple date cell, usage date cell, badge date cell,\ndetail date cell.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/month-sheet-view.png\" alt=\"Month Sheet View\">\n</div>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/month-sheet-view-aligned.png\" alt=\"Month Sheet View Aligned\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">YearView</dt>\n<dd>\n<p>Shows twelve <code>YearMonthView</code> instances.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/year-view.png\" alt=\"Year View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">YearMonthView</dt>\n<dd>\n<p>Sort of a date picker control. 12 instances of this control are used to build up the <code>YearPage</code> control. This control provides many properties for easy\ncustomization. The month label, the year label, and the arrow buttons can be hidden. A cell factory can be set to customize the appearance of each day, and so on.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/date-picker.png\" alt=\"Year Month View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">AllDayView</dt>\n<dd>\n<p>Just like the <code>WeekView</code> this control can also span multiple days. It is being used as a header for the <code>DayView</code> inside the <code>DayPage</code> and also for the <code>WeekView</code> inside the <code>WeekPage</code>. The control displays calendar entries that have their \"full day\" property set to true.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/all-day-view.png\" alt=\"All Day View\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">CalendarHeaderView</dt>\n<dd>\n<p>Displays the names of all currently visible calendars, but only when the <code>DateControl</code> has its layout set to <code>SWIMLANE</code> and not to <code>STANDARD</code>.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/calendar-header-view.png\" alt=\"Calendar Header View\">\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_calendar_pages\">Calendar Pages</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Calendar pages are complex controls that are composed of several controls, many of them <code>DateControl</code> instances. All pages provide controls to navigate to different\ndates or to quickly jump to \"Today\". Each page also shows a title with the current date shown. The <code>CalendarView</code> class manages one instance of each page type to let the\nuser switch from a day, to a week, to a month, to a year.</p>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">DayPage</dt>\n<dd>\n<p>Shows an <code>AgendaView</code>, a <code>DetailedDayView</code>, and a <code>YearMonthView</code>. This page is designed to give the user a quick overview of what is going on today and\nin the near future (agenda).</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/day-page.png\" alt=\"Day Page\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">WeekPage</dt>\n<dd>\n<p>Composed of a <code>DetailedWeekView</code>.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/week-page.png\" alt=\"Week Page\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">MonthPage</dt>\n<dd>\n<p>Shows a single <code>MonthView</code> control.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/month-page.png\" alt=\"Month Page\">\n</div>\n</div>\n<div class=\"dlist\">\n<dl>\n<dt class=\"hdlist1\">YearPage</dt>\n<dd>\n<p>Shows a <code>YearView</code> with twelve <code>YearMonthView</code> sub-controls. Alternatively can switch to a <code>MonthSheetView</code>.</p>\n</dd>\n</dl>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/year-page.png\" alt=\"Year Page using YearView\">\n</div>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/year-page-2.png\" alt=\"Year Page using MonthSheetView\">\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_resource_scheduling_views\">Resource Scheduling Views</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Another category of views is used for scheduling resource allocations. These are commonly used by scheduling software, e.g. a customer appointment application for a garage, a hairdresser, and so on. The</p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_resourcesview\">ResourcesView</h3>\n<div class=\"paragraph\">\n<p>The class <code>ResourcesView</code> displays one or more days for one or more resources. The view can either display one or more resources for a given\nday (<code>ResourceView.Type.RESOURCES_OVER_DATES</code>) or one or more days for a given resource (<code>ResourceView.Type.DATES_OVER_RESOURCE</code>). Each one of\nthese options can be configured to show one or more dates and one or more resources.</p>\n</div>\n<div class=\"paragraph\">\n<p>This screenshot shows the resources view when the type of the view has been set to \"resources over dates\".</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/resources-view-resources-over-dates.png\" alt=\"ResourcesView - Resources over Dates\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>The next screenshot shows the resources view when the type of the view has been set to \"dates over resources\".</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/resources-view-dates-over-resources.png\" alt=\"esourcesView - Dates over Resources\">\n</div>\n</div>\n<div class=\"paragraph\">\n<p>By default, the calendar entries will become semi-transparent when the user switches to the \"edit availability\" mode. This behaviour can be configured so that either the entries stay completely visible or they are completely hidden. The following screenshot shows the situation where the user is editing the resources' availability and the already existing calendar entries become semi-transparent.</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/resources-view-availability.png\" alt=\"esourcesView - Availability Editing\">\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_developer_console\">Developer Console</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><em>CalendarFX</em> supports a special system property called <code>calendarfx.developer</code>. If this property is set to <code>true</code> then a developer console is being added to the skin of <code>CalendarView</code>. The console can be made visible by pressing <code>SHORTCUT-D</code>. The console is a standard <em>CalendarFX</em> control and you can also add it directly to your application for development purposes.</p>\n</div>\n<div class=\"imageblock text-center thumb\">\n<div class=\"content\">\n<img src=\"manual-images/developer-console.png\" alt=\"Developer Console\">\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_logging\">Logging</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><em>CalendarFX</em> uses the standard java logging api for its logging. The logging settings and the available loggers can be found inside the file <code>logging.properties</code>. <em>CalendarFX</em> uses domains for logging and not packages or classes. Several domains are available: view, model, editing, recurrence, etc&#8230;&#8203;</p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_internationalization_i18n\">Internationalization (i18n)</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>The default resource bundle of <em>CalendarFX</em> is English. Additional bundles include German, Spanish, French, Italian, Portuguese (Brazil), and Czech.\nAll can be found in the distribution (misc/messages.properties, misc/messages_de.properties, etc&#8230;&#8203;). Please submit a pull request to add another\nlanguage to <em>CalendarFX</em>.</p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_known_issues\">Known Issues</h2>\n<div class=\"sectionbody\">\n<div class=\"ulist\">\n<ul>\n<li>\n<p>There is currently no support for defining exceptions for recurrence rules. In most calendar applications, when the user edits a recurrent entry, the user will be asked whether he wants to change just this one recurrence or the whole series. This feature is currently not supported but will be in one of the next releases.</p>\n</li>\n<li>\n<p>In <code>SwimLane</code> layout it would be nice if the user could drag an entry horizontally from one column / calendar to another. This is currently not supported. We will investigate if this can be added in one of the next releases.</p>\n</li>\n</ul>\n</div>\n</div>\n</div>\n</div>\n<div id=\"footer\">\n<div id=\"footer-text\">\nLast updated 2022-10-06 15:21:21 +0200\n</div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "formatter-settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"12\">\n    <profile kind=\"CodeFormatterProfile\" name=\"ControlsFX\" version=\"12\">\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\"\n                 value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\"\n                 value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\"\n                 value=\"do not insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\"\n                 value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n    </profile>\n    <profile kind=\"CodeFormatterProfile\" name=\"FlexGanttFXFormatter\" version=\"12\">\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\"\n                 value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\"\n                 value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\"\n                 value=\"do not insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\"\n                 value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.7\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n        <setting\n                id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\"\n                value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\"\n                 value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\"\n                 value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"tab\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\"\n                 value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n    </profile>\n</profiles>\n"
  },
  {
    "path": "jreleaser.yml",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\n#\n# Copyright 2021 Dirk Lemmermann Software & Consulting (dlsc.com)\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nproject:\n  name: GemsFX\n  description: A Java framework for creating sophisticated calendar views.\n  website: https://github.com/dlsc-software-consulting-gmbh/GemsFX/\n  authors:\n    - Dirk Lemmermann\n  license: Apache-2.0\n  extraProperties:\n    inceptionYear: 2022\n\nrelease:\n  github:\n    branch: master-11\n    overwrite: true\n    milestone:\n      name: '{{projectVersion}}'\n    changelog:\n      sort: DESC\n      formatted: ALWAYS\n      format: '- {{commitShortHash}} {{commitTitle}}'\n      contributors:\n        format: '- {{contributorName}}{{#contributorUsernameAsLink}} ({{.}}){{/contributorUsernameAsLink}}'\n      hide:\n        contributors:\n          - 'GitHub'"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /usr/local/etc/mavenrc ] ; then\n    . /usr/local/etc/mavenrc\n  fi\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`\\\\unset -f command; \\\\command -v java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\n##########################################################################################\n# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n# This allows using the maven wrapper in projects that prohibit checking in binary data.\n##########################################################################################\nif [ -r \"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\" ]; then\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Found .mvn/wrapper/maven-wrapper.jar\"\n    fi\nelse\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ...\"\n    fi\n    if [ -n \"$MVNW_REPOURL\" ]; then\n      jarUrl=\"$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    else\n      jarUrl=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    fi\n    while IFS=\"=\" read key value; do\n      case \"$key\" in (wrapperUrl) jarUrl=\"$value\"; break ;;\n      esac\n    done < \"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties\"\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Downloading from: $jarUrl\"\n    fi\n    wrapperJarPath=\"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\"\n    if $cygwin; then\n      wrapperJarPath=`cygpath --path --windows \"$wrapperJarPath\"`\n    fi\n\n    if command -v wget > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found wget ... using wget\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            wget \"$jarUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n        else\n            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD \"$jarUrl\" -O \"$wrapperJarPath\" || rm -f \"$wrapperJarPath\"\n        fi\n    elif command -v curl > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found curl ... using curl\"\n        fi\n        if [ -z \"$MVNW_USERNAME\" ] || [ -z \"$MVNW_PASSWORD\" ]; then\n            curl -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        else\n            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o \"$wrapperJarPath\" \"$jarUrl\" -f\n        fi\n\n    else\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Falling back to using Java to download\"\n        fi\n        javaClass=\"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java\"\n        # For Cygwin, switch paths to Windows format before running javac\n        if $cygwin; then\n          javaClass=`cygpath --path --windows \"$javaClass\"`\n        fi\n        if [ -e \"$javaClass\" ]; then\n            if [ ! -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Compiling MavenWrapperDownloader.java ...\"\n                fi\n                # Compiling the Java class\n                (\"$JAVA_HOME/bin/javac\" \"$javaClass\")\n            fi\n            if [ -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                # Running the downloader\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Running MavenWrapperDownloader.java ...\"\n                fi\n                (\"$JAVA_HOME/bin/java\" -cp .mvn/wrapper MavenWrapperDownloader \"$MAVEN_PROJECTBASEDIR\")\n            fi\n        fi\n    fi\nfi\n##########################################################################################\n# End of extension\n##########################################################################################\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nif [ \"$MVNW_VERBOSE\" = true ]; then\n  echo $MAVEN_PROJECTBASEDIR\nfi\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\n# Provide a \"standardized\" way to retrieve the CLI args that will\n# work with both Windows and non-Windows executions.\nMAVEN_CMD_LINE_ARGS=\"$MAVEN_CONFIG $@\"\nexport MAVEN_CMD_LINE_ARGS\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  $MAVEN_DEBUG_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \\\n  \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM set title of command window\ntitle %0\n@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%USERPROFILE%\\mavenrc_pre.bat\" call \"%USERPROFILE%\\mavenrc_pre.bat\" %*\nif exist \"%USERPROFILE%\\mavenrc_pre.cmd\" call \"%USERPROFILE%\\mavenrc_pre.cmd\" %*\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nset DOWNLOAD_URL=\"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n\nFOR /F \"usebackq tokens=1,2 delims==\" %%A IN (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties\") DO (\n    IF \"%%A\"==\"wrapperUrl\" SET DOWNLOAD_URL=%%B\n)\n\n@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n@REM This allows using the maven wrapper in projects that prohibit checking in binary data.\nif exist %WRAPPER_JAR% (\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Found %WRAPPER_JAR%\n    )\n) else (\n    if not \"%MVNW_REPOURL%\" == \"\" (\n        SET DOWNLOAD_URL=\"%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar\"\n    )\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Couldn't find %WRAPPER_JAR%, downloading it ...\n        echo Downloading from: %DOWNLOAD_URL%\n    )\n\n    powershell -Command \"&{\"^\n\t\t\"$webclient = new-object System.Net.WebClient;\"^\n\t\t\"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {\"^\n\t\t\"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');\"^\n\t\t\"}\"^\n\t\t\"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')\"^\n\t\t\"}\"\n    if \"%MVNW_VERBOSE%\" == \"true\" (\n        echo Finished downloading %WRAPPER_JAR%\n    )\n)\n@REM End of extension\n\n@REM Provide a \"standardized\" way to retrieve the CLI args that will\n@REM work with both Windows and non-Windows executions.\nset MAVEN_CMD_LINE_ARGS=%*\n\n%MAVEN_JAVA_EXE% ^\n  %JVM_CONFIG_MAVEN_PROPS% ^\n  %MAVEN_OPTS% ^\n  %MAVEN_DEBUG_OPTS% ^\n  -classpath %WRAPPER_JAR% ^\n  \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" ^\n  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\"==\"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%USERPROFILE%\\mavenrc_post.bat\" call \"%USERPROFILE%\\mavenrc_post.bat\"\nif exist \"%USERPROFILE%\\mavenrc_post.cmd\" call \"%USERPROFILE%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\"==\"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\"==\"on\" exit %ERROR_CODE%\n\ncmd /C exit /B %ERROR_CODE%\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>com.calendarfx</groupId>\n    <artifactId>calendar</artifactId>\n    <version>12.0.1</version>\n    <packaging>pom</packaging>\n    <name>CalendarFX</name>\n    <description>The parent project for the various CalendarFX modules.\n    </description>\n    <organization>\n        <name>Dirk Lemmermann Software &amp; Consulting</name>\n    </organization>\n\n    <parent>\n        <groupId>com.dlsc</groupId>\n        <artifactId>dlsc-maven-parent</artifactId>\n        <version>1.6.0</version>\n    </parent>\n\n    <url>https://github.com/dlsc-software-consulting-gmbh/CalendarFX</url>\n\n    <properties>\n        <project.identifier>calendarfx</project.identifier>\n        <project.github.repository>dlsc-software-consulting-gmbh/CalendarFX\n        </project.github.repository>\n        <local.repository.path>/tmp/repository</local.repository.path>\n        <java.version>21</java.version>\n        <javafx.version>23.0.2</javafx.version>\n        <ikonli.version>12.4.0</ikonli.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>\n        <controlsfx.version>11.2.2</controlsfx.version>\n        <ical4j.version>4.1.1</ical4j.version>\n    </properties>\n\n    <licenses>\n        <license>\n            <name>Apache 2.0</name>\n        </license>\n    </licenses>\n\n    <scm>\n        <url>https://github.com/dlsc-software-consulting-gmbh/CalendarFX</url>\n    </scm>\n\n    <developers>\n        <developer>\n            <name>Dirk Lemmermann</name>\n            <url>https://www.dlsc.com</url>\n            <organization>DLSC Software &amp; Consulting</organization>\n        </developer>\n    </developers>\n\n    <modules>\n        <module>CalendarFXView</module>\n    </modules>\n\n    <profiles>\n        <profile>\n            <id>all-modules</id>\n            <activation>\n                <property>\n                    <name>release</name>\n                    <value>!true</value>\n                </property>\n            </activation>\n            <modules>\n                <module>CalendarFXSampler</module>\n                <module>CalendarFXGoogle</module>\n                <module>CalendarFXApp</module>\n                <module>CalendarFXSchedulerApp</module>\n                <module>CalendarFXResourceApp</module>\n                <module>CalendarFXiCal</module>\n                <module>CalendarFXWeather</module>\n            </modules>\n        </profile>\n    </profiles>\n\n    <dependencyManagement>\n        <dependencies>\n\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>3.14.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>javax.measure</groupId>\n                <artifactId>unit-api</artifactId>\n                <version>2.1.2</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.dlsc.gemsfx</groupId>\n                <artifactId>gemsfx</artifactId>\n                <version>3.5.5</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-validator</groupId>\n                <artifactId>commons-validator</artifactId>\n                <version>1.8.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-logging</groupId>\n                <artifactId>commons-logging</artifactId>\n                <version>1.3.1</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.github.mkpaz</groupId>\n                <artifactId>atlantafx-base</artifactId>\n                <version>2.0.1</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.j2objc</groupId>\n                <artifactId>j2objc-annotations</artifactId>\n                <version>2.8</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-stdlib</artifactId>\n                <version>1.6.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.jetbrains.kotlin</groupId>\n                <artifactId>kotlin-stdlib-common</artifactId>\n                <version>1.4.10</version>\n            </dependency>\n\n            <dependency>\n                <groupId>fr.brouillard.oss</groupId>\n                <artifactId>cssfx</artifactId>\n                <version>11.5.1</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.controlsfx</groupId>\n                <artifactId>fxsampler</artifactId>\n                <version>1.0.11</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.kordamp.ikonli</groupId>\n                <artifactId>ikonli-javafx</artifactId>\n                <version>${ikonli.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.kordamp.ikonli</groupId>\n                <artifactId>ikonli-fontawesome-pack</artifactId>\n                <version>${ikonli.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.controlsfx</groupId>\n                <artifactId>controlsfx</artifactId>\n                <version>${controlsfx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-base</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-controls</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-graphics</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-fxml</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-media</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-web</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.openjfx</groupId>\n                <artifactId>javafx-swing</artifactId>\n                <version>${javafx.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.calendarfx</groupId>\n                <artifactId>view</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.calendarfx</groupId>\n                <artifactId>application</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.kordamp.ikonli</groupId>\n                <artifactId>ikonli-weathericons-pack</artifactId>\n                <version>${ikonli.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>4.13.2</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.hamcrest</groupId>\n                <artifactId>hamcrest-all</artifactId>\n                <version>1.3</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mockito</groupId>\n                <artifactId>mockito-all</artifactId>\n                <version>2.0.2-beta</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mnode.ical4j</groupId>\n                <artifactId>ical4j</artifactId>\n                <version>${ical4j.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.dlsc</groupId>\n                <artifactId>GMapsFX</artifactId>\n                <version>11.0.6</version>\n            </dependency>\n\n            <!-- Required for GMapsFX -->\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>2.0.17</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-simple</artifactId>\n                <version>2.0.17</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.api-client</groupId>\n                <artifactId>google-api-client</artifactId>\n                <version>1.30.4</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.api-client</groupId>\n                <artifactId>google-api-client-java6</artifactId>\n                <version>1.30.4</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.api-client</groupId>\n                <artifactId>google-api-client-jackson2</artifactId>\n                <version>1.30.4</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.apis</groupId>\n                <artifactId>google-api-services-calendar</artifactId>\n                <version>v3-rev20190910-1.30.1</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.apis</groupId>\n                <artifactId>google-api-services-oauth2</artifactId>\n                <version>v2-rev20190313-1.30.1</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.code.geocoder-java</groupId>\n                <artifactId>geocoder-java</artifactId>\n                <version>0.16</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.code.gson</groupId>\n                <artifactId>gson</artifactId>\n                <version>2.8.9</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-codec</groupId>\n                <artifactId>commons-codec</artifactId>\n                <version>1.19.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.opencensus</groupId>\n                <artifactId>opencensus-api</artifactId>\n                <version>0.30.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.maps</groupId>\n                <artifactId>google-maps-services</artifactId>\n                <version>2.0.0</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>32.0.1-android</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-logging</groupId>\n                <artifactId>commons-logging</artifactId>\n                <version>1.2</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <!-- Activate the use of TCP to transmit events to the plugin -->\n                    <forkNode implementation=\"org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory\"/>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <dependencies>\n        <!-- Test Dependencies -->\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.hamcrest</groupId>\n            <artifactId>hamcrest-all</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-all</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n</project>\n"
  },
  {
    "path": "scenicView.properties",
    "content": "#ScenicView properties\n#Wed Sep 10 17:38:04 CEST 2025\nautoRefreshStyleSheets=false\nautomaticScenegraphStructureRefreshing=true\ncollapseContainerControls=false\ncollapseControls=true\nignoreMouseTransparentNodes=true\nregisterShortcuts=true\nshowBaseline=false\nshowBounds=true\nshowCSSProperties=false\nshowDefaultProperties=true\nshowFilteredNodesInTree=true\nshowInvisibleNodes=false\nshowNodesIdInTree=false\nshowSearchBar=true\nsplitPaneDividerPosition=0.5\nstageHeight=984.0\nstageWidth=1317.0\n"
  },
  {
    "path": "settings.xml",
    "content": "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd\">\n    <servers>\n        <server>\n            <id>central</id>\n            <username>${MAVEN_USERNAME}</username>\n            <password>${MAVEN_CENTRAL_TOKEN}</password>\n        </server>\n    </servers>\n</settings>"
  }
]