[
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n  schedule:\n    - cron: '15 3 * * 1'\n\njobs:\n  analyze:\n    name: Analyze\n    # Runner size impacts CodeQL analysis time. To learn more, please see:\n    #   - https://gh.io/recommended-hardware-resources-for-running-codeql\n    #   - https://gh.io/supported-runners-and-hardware-resources\n    #   - https://gh.io/using-larger-runners\n    # Consider using larger runners for possible analysis time improvements.\n    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}\n    timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}\n    permissions:\n      # required for all workflows\n      security-events: write\n\n      # only required for workflows in private repositories\n      actions: read\n      contents: read\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'java-kotlin' ]\n        # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]\n        # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both\n        # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    - name: Setup Java JDK\n      uses: actions/setup-java@v4.0.0\n      with:\n    # The Java version to set up. Takes a whole or semver Java version. See examples of supported syntax in README file\n        java-version: 17\n        distribution: oracle\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n\n    # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v3\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n    #   If the Autobuild fails above, remove it and uncomment the following three lines.\n    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n    # - run: |\n    #     echo \"Run, Build Application using script\"\n    #     ./location_of_script_within_repo/buildscript.sh\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/codeql2.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL Advanced\"\n\non:\n  push:\n    branches: [ \"2_0_master\" ]\n  pull_request:\n    branches: [ \"2_0_master\" ]\n  schedule:\n    - cron: '35 23 * * 1'\n\njobs:\n  analyze:\n    name: Analyze (${{ matrix.language }})\n    # Runner size impacts CodeQL analysis time. To learn more, please see:\n    #   - https://gh.io/recommended-hardware-resources-for-running-codeql\n    #   - https://gh.io/supported-runners-and-hardware-resources\n    #   - https://gh.io/using-larger-runners (GitHub.com only)\n    # Consider using larger runners or machines with greater resources for possible analysis time improvements.\n    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}\n    permissions:\n      # required for all workflows\n      security-events: write\n\n      # required to fetch internal or private CodeQL packs\n      packages: read\n\n      # only required for workflows in private repositories\n      actions: read\n      contents: read\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n        - language: actions\n          build-mode: none\n        - language: java-kotlin\n          build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too.\n        # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'\n        # Use `c-cpp` to analyze code written in C, C++ or both\n        # Use 'java-kotlin' to analyze code written in Java, Kotlin or both\n        # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both\n        # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,\n        # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.\n        # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how\n        # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    # Add any setup steps before running the `github/codeql-action/init` action.\n    # This includes steps like installing compilers or runtimes (`actions/setup-node`\n    # or others). This is typically only required for manual builds.\n    # - name: Setup runtime (example)\n    #   uses: actions/setup-example@v1\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n        build-mode: ${{ matrix.build-mode }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n    # If the analyze step fails for one of the languages you are analyzing with\n    # \"We were unable to automatically build your code\", modify the matrix above\n    # to set the build mode to \"manual\" for that language. Then modify this step\n    # to build your code.\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n    - name: Run manual build steps\n      if: matrix.build-mode == 'manual'\n      shell: bash\n      run: |\n        echo 'If you are using a \"manual\" build mode for one or more of the' \\\n          'languages you are analyzing, replace this with the commands to build' \\\n          'your code, for example:'\n        echo '  make bootstrap'\n        echo '  make release'\n        exit 1\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nborghelp/\ndist/\n*.class\nclasses/\ntarget/\nbin/\nLICENSE.txt\n/.idea/\n*.iml\n/desktop.ini\n*.msi\n/BORGCalendar/.project\n/BORGCalendar/.settings/org.eclipse.core.resources.prefs\n/BORGCalendar/.settings/org.eclipse.m2e.core.prefs\n/BORGCalendar/common/.project\n/BORGCalendar/common/.settings/org.eclipse.core.resources.prefs\n/BORGCalendar/common/.settings/org.eclipse.jdt.core.prefs\n/BORGCalendar/common/.settings/org.eclipse.m2e.core.prefs\n/BORGCalendar/install/.project\n/BORGCalendar/install/.settings/org.eclipse.core.resources.prefs\n/BORGCalendar/install/.settings/org.eclipse.jdt.core.prefs\n/BORGCalendar/install/.settings/org.eclipse.m2e.core.prefs\n/BORGCalendar/model/.project\n/BORGCalendar/model/.settings/org.eclipse.core.resources.prefs\n/BORGCalendar/model/.settings/org.eclipse.jdt.core.prefs\n/BORGCalendar/model/.settings/org.eclipse.m2e.core.prefs\n/BORGCalendar/swingui/.project\n/BORGCalendar/swingui/.settings/org.eclipse.core.resources.prefs\n/BORGCalendar/swingui/.settings/org.eclipse.jdt.core.prefs\n/BORGCalendar/swingui/.settings/org.eclipse.m2e.core.prefs\n"
  },
  {
    "path": "BORGCalendar/common/.gitignore",
    "content": "/.classpath\n"
  },
  {
    "path": "BORGCalendar/common/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>BORGCalendar</groupId>\n\t\t<artifactId>BORGCalendar</artifactId>\n\t\t<version>2.0</version>\n\t</parent>\n\t<artifactId>common</artifactId>\n\t<properties>\n\t\t<timestamp>${maven.build.timestamp}</timestamp>\n\t\t<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>\n\t</properties>\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>javax.mail</groupId>\n\t\t\t<artifactId>mail</artifactId>\n\t\t\t<version>1.5.0-b01</version>\n\t\t</dependency>\n\t</dependencies>\n\t<build>\n\t\t<resources>\n\t\t\t<resource>\n\t\t\t\t<directory>src/main/resources</directory>\n\t\t\t\t<includes>\n\t\t\t\t\t<include>**/*</include>\n\t\t\t\t</includes>\n\t\t\t\t<filtering>true</filtering>\n\t\t\t</resource>\n\t\t</resources>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.codehaus.mojo</groupId>\n\t\t\t\t<artifactId>buildnumber-maven-plugin</artifactId>\n\t\t\t\t<version>1.4</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>initialize</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>create</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<timestampFormat>{0, date, yyyy-MM-dd HH:mm:ss}</timestampFormat>\n\t\t\t\t\t<revisionOnScmFailure>no-git-found</revisionOnScmFailure>\n\t\t\t\t\t<doCheck>false</doCheck>\n\t\t\t\t\t<doUpdate>false</doUpdate>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<scm>\n\t\t<url>https://github.com/mikeberger/borg_calendar.git</url>\n\t\t<connection>scm:git:https://github.com/mikeberger/borg_calendar.git</connection>\n\t</scm>\n</project>"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/DateUtil.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.common;\r\n\r\nimport java.util.Calendar;\r\nimport java.util.Date;\r\nimport java.util.GregorianCalendar;\r\n\r\n/**\r\n * Some common date utility logic\r\n */\r\npublic class DateUtil {\r\n\r\n\t/**\r\n\t * Checks if one date falls on a later calendar day than another.\r\n\t * \r\n\t * @param d1\r\n\t *            the first date\r\n\t * @param d2\r\n\t *            the second date\r\n\t * \r\n\t * @return true, if is after\r\n\t */\r\n\tpublic static boolean isAfter(Date d1, Date d2) {\r\n\r\n\t\tGregorianCalendar tcal = new GregorianCalendar();\r\n\t\ttcal.setTime(d1);\r\n\t\ttcal.set(Calendar.HOUR_OF_DAY, 0);\r\n\t\ttcal.set(Calendar.MINUTE, 0);\r\n\t\ttcal.set(Calendar.SECOND, 0);\r\n\t\tGregorianCalendar dcal = new GregorianCalendar();\r\n\t\tdcal.setTime(d2);\r\n\t\tdcal.set(Calendar.HOUR_OF_DAY, 0);\r\n\t\tdcal.set(Calendar.MINUTE, 10);\r\n\t\tdcal.set(Calendar.SECOND, 0);\r\n\r\n\t\treturn tcal.getTime().after(dcal.getTime());\r\n\t}\r\n\t\r\n\t/**\r\n\t * set a date to midnight\r\n\t * @param d - the date\r\n\t */\r\n\tstatic public Date setToMidnight(Date d)\r\n\t{\r\n\t\tGregorianCalendar cal = new GregorianCalendar();\r\n\t\tcal.setTime(d);\r\n\t\tcal.set(Calendar.HOUR_OF_DAY, 0);\r\n\t\tcal.set(Calendar.MINUTE, 0);\r\n\t\tcal.set(Calendar.SECOND, 0);\r\n\t\tcal.set(Calendar.MILLISECOND, 0);\r\n\t\treturn cal.getTime();\r\n\t}\r\n\r\n\t/**\r\n\t * return the number of the day of the epoch for a given date. this provides\r\n\t * a decent Date to int converter that returns the same value for all Dates\r\n\t * on a given day.\r\n\t * \r\n\t * @param d\r\n\t *            the date\r\n\t * \r\n\t * @return the days from the beginning of the epoch until d\r\n\t */\r\n\tstatic public int dayOfEpoch(Date d) {\r\n\t\t// adjust to local time\r\n\t\tGregorianCalendar cal = new GregorianCalendar();\r\n\t\tcal.setTime(d);\r\n\t\tcal.set(Calendar.HOUR_OF_DAY, 11);\r\n\t\treturn (int) (cal.getTime().getTime() / 1000 / 60 / 60 / 24);\r\n\t}\r\n\t\r\n\t/**\r\n\t * generate a human readable string for a particular number of minutes\r\n\t * \r\n\t * @param mins - the number of minutes\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tpublic static String minuteString(int mins) {\r\n\t\t\r\n\t\tint hours = mins / 60;\r\n\t\tint minsPast = mins % 60;\r\n\t\t\r\n\t\tString minutesString;\r\n\t\tString hoursString;\r\n\t\t\r\n\t\tif (hours > 1) {\r\n\t\t\thoursString = hours + \" \" + Resource.getResourceString(\"Hours\");\r\n\t\t} else if (hours > 0) {\r\n\t\t\thoursString = hours + \" \" + Resource.getResourceString(\"Hour\");\r\n\t\t} else {\r\n\t\t\thoursString = \"\";\r\n\t\t}\r\n\r\n\t\tif (minsPast > 1) {\r\n\t\t\tminutesString = minsPast + \" \" + Resource.getResourceString(\"Minutes\");\r\n\t\t} else if (minsPast > 0) {\r\n\t\t\tminutesString = minsPast + \" \" + Resource.getResourceString(\"Minute\");\r\n\t\t} else if (hours >= 1) {\r\n\t\t\tminutesString = \"\";\r\n\t\t} else {\r\n\t\t\tminutesString = minsPast + \" \" + Resource.getResourceString(\"Minutes\");\r\n\t\t}\r\n\r\n\t\t// space between hours and minutes\r\n\t\tif (!hoursString.equals(\"\") && !minutesString.equals(\"\"))\r\n\t\t\tminutesString = \" \" + minutesString;\r\n\r\n\t\treturn hoursString + minutesString;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * return the number of days left before a given date.\r\n\t *\r\n\t * @param dd the date\r\n\t * @return the number of days left\r\n\t */\r\n\tpublic static int daysLeft(Date dd) {\r\n\r\n\t\tif (dd == null)\r\n\t\t\treturn 0;\r\n\t\tCalendar today = new GregorianCalendar();\r\n\t\tCalendar dcal = new GregorianCalendar();\r\n\t\tdcal.setTime(dd);\r\n\r\n\t\t// find days left\r\n\t\tint days = 0;\r\n\t\tif (dcal.get(Calendar.YEAR) == today.get(Calendar.YEAR)) {\r\n\t\t\tdays = dcal.get(Calendar.DAY_OF_YEAR)\r\n\t\t\t\t\t- today.get(Calendar.DAY_OF_YEAR);\r\n\t\t} else {\r\n\t\t\tdays = Long.valueOf((dd.getTime() - today.getTime().getTime())\r\n\t\t\t\t\t/ (1000 * 60 * 60 * 24)).intValue();\r\n\t\t}\r\n\r\n\t\t// if due date is past, set days left to 0\r\n\t\t// negative days are silly\r\n\t\tif (days < 0)\r\n\t\t\tdays = 0;\r\n\t\treturn days;\r\n\t}\r\n\r\n\t/**\r\n\t * determine the number fo days between two dates\r\n\t *\r\n\t * @param start the first date\r\n\t * @param dd    the later date\r\n\t * @return the int\r\n\t */\r\n\tpublic static int daysBetween(Date start, Date dd) {\r\n\r\n\t\tif (dd == null)\r\n\t\t\treturn 0;\r\n\t\tCalendar startcal = new GregorianCalendar();\r\n\t\tCalendar dcal = new GregorianCalendar();\r\n\t\tdcal.setTime(dd);\r\n\t\tstartcal.setTime(start);\r\n\r\n\t\t// find days left\r\n\t\tint days = 0;\r\n\t\tif (dcal.get(Calendar.YEAR) == startcal.get(Calendar.YEAR)) {\r\n\t\t\tdays = dcal.get(Calendar.DAY_OF_YEAR)\r\n\t\t\t\t\t- startcal.get(Calendar.DAY_OF_YEAR);\r\n\t\t} else {\r\n\t\t\tdays = Long.valueOf((dd.getTime() - startcal.getTime().getTime())\r\n\t\t\t\t\t/ (1000 * 60 * 60 * 24)).intValue();\r\n\t\t}\r\n\r\n\t\t// if due date is past, set days left to 0\r\n\t\t// negative days are silly\r\n\t\tif (days < 0)\r\n\t\t\tdays = 0;\r\n\t\treturn days;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/EncryptionHelper.java",
    "content": "package net.sf.borg.common;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherOutputStream;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.*;\nimport java.security.Key;\nimport java.security.KeyStore;\nimport java.util.Base64;\n\n\n/**\n * class containing encryption and decryption methods for borg\n * \n */\npublic class EncryptionHelper {\n\t\n    static final String borg_key_alias = \"borg_key\";\n\n\t/* the cached keystore object */\n\tprivate final KeyStore keyStore;\n\t\n\t/* cached password */\n    private final String password;\n    \n\t/**\n\t * create a new JCEKS Key Store \n\t * @param location - location (file) for the key store\n\t * @param password - key store password\n\t * @throws Exception\n\t */\n\tstatic public void createStore(String location, String password)\n\t\t\tthrows Exception {\n\t\tKeyStore store = KeyStore.getInstance(\"JCEKS\");\n\t\tstore.load(null, password.toCharArray());\n\t\tFileOutputStream fos = new FileOutputStream(location);\n\t\tstore.store(fos, password.toCharArray());\n\t\tfos.close();\n\t}\n\t\n\tstatic public void createDefaultStore(String location, String password) throws Exception {\n\t\tcreateStore(location, password);\n\t\tgenerateKey(location,password,borg_key_alias);\n\t}\n\n\t\n\t/**\n\t * generate a new encryption key in the key store. the key store password will\n\t * be used as the key password.\n\t * @param location - key store location\n\t * @param password - key store password\n\t * @param name - key alias\n\t * @throws Exception\n\t */\n\tstatic public void generateKey(String location, String password, String name)\n\t\t\tthrows Exception {\n\t\tKeyStore store = KeyStore.getInstance(\"JCEKS\");\n\t\tFileInputStream fis = new FileInputStream(location);\n\t\tstore.load(fis, password.toCharArray());\n\t\tfis.close();\n\n\t\tKeyGenerator keyGen = KeyGenerator.getInstance(\"AES\");\n\t\tSecretKey key = keyGen.generateKey();\n\t\t\n\t\tKeyStore.SecretKeyEntry skEntry =\n\t        new KeyStore.SecretKeyEntry(key);\n\t\tstore.setEntry(name, skEntry, \n\t        new KeyStore.PasswordProtection(password.toCharArray()));\n\t\tFileOutputStream fos = new FileOutputStream(location);\n\t\tstore.store(fos, password.toCharArray());\n\t\tfos.close();\n\n\t}\n\n\tpublic EncryptionHelper(String keyStorePassword) throws Exception {\n\t\tthis(Prefs.getPref(PrefName.KEYSTORE), keyStorePassword);\n\t}\n\t\n\t/**\n\t * constructor - loads a KeyStore from a file\n\t * @param keyStoreLocation - key store location\n\t * @param keyStorePassword - key store password\n\t * @throws Exception\n\t */\n\tpublic EncryptionHelper(String keyStoreLocation, String keyStorePassword)\n\t\t\tthrows Exception {\n\t\t\n\t\tthis.password = keyStorePassword;\n\t\t\n\t\t\n\t\tif( keyStoreLocation == null || keyStoreLocation.equals(\"\"))\n\t\t\tthrow new Warning(Resource.getResourceString(\"Key_Store_Not_Set\"));\n\t\t\n\t\tFile f = new File(keyStoreLocation);\n\t\tif( !f.canRead())\n\t\t{\n\t\t\tthrow new Warning(Resource.getResourceString(\"No_Key_Store\") + keyStoreLocation);\n\t\t}\n\t\tthis.keyStore = KeyStore.getInstance(\"JCEKS\");\n\t\tFileInputStream fis = new FileInputStream(keyStoreLocation);\n\t\tthis.keyStore.load(fis, this.password\n\t\t\t\t.toCharArray());\n\t\tfis.close();\n\t}\n\t\n\tpublic String encrypt(String clearText) throws Exception {\n\t\treturn encrypt(clearText,borg_key_alias);\n\t}\n\n\t/**\n\t * encrypt a String using a key from the key store\n\t * @param clearText - the string to encrypt\n\t * @param keyAlias - the encryption key alias\n\t * @return the encrypted string\n\t * @throws Exception\n\t */\n\tpublic String encrypt(String clearText, String keyAlias)\n\t\t\tthrows Exception {\n\n\t\t/*\n\t\t * get the key and create the Cipher\n\t\t */\n\t\tKey key = this.keyStore.getKey(keyAlias, this.password.toCharArray());\n\t\tCipher enc = Cipher.getInstance(\"AES\");\n\t\tenc.init(Cipher.ENCRYPT_MODE, key);\n\n\t\t/*\n\t\t * encrypt the clear text\n\t\t */\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tOutputStream os = new CipherOutputStream(baos, enc);\n\t\tos.write(clearText.getBytes());\n\t\tos.close();\n\n\t\t/*\n\t\t * get the encrypted bytes and encode to a string\n\t\t */\n\t\tbyte[] ba = baos.toByteArray();\n\t\treturn new String(Base64.getEncoder().encode(ba));\n\n\t}\n\t\n\tpublic String decrypt(String clearText) throws Exception {\n\t\treturn decrypt(clearText,borg_key_alias);\n\t}\n\n\t/**\n\t * decrypt a String using a key from the key store\n\t * @param cipherText - the string to decrypt\n\t * @param keyAlias - the decryption key alias\n\t * @return the encrypted string\n\t * @throws Exception\n\t */\n\tpublic String decrypt(String cipherText, String keyAlias)\n\t\t\tthrows Exception {\n\n\t\t/*\n\t\t * get the key and create the Cipher\n\t\t */\n\t\tKey key = this.keyStore.getKey(keyAlias, this.password.toCharArray());\n\t\tCipher dec = Cipher.getInstance(\"AES\");\n\t\tdec.init(Cipher.DECRYPT_MODE, key);\n\n\t\t/*\n\t\t * decode the cipher text from base64 back to a byte array\n\t\t */\n\t\tbyte[] decba = Base64.getDecoder().decode(cipherText);\n\n\t\t/*\n\t\t * decrpyt the bytes\n\t\t */\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tOutputStream os = new CipherOutputStream(baos, dec);\n\t\tos.write(decba);\n\t\tos.close();\n\n\t\treturn baos.toString();\n\t}\n\t\n\t/**\n\t * Export the borg key in text form\n\t * @param keyAlias the key alias\n\t * @param keyStorePassword the keystore password\n\t * @return the exproted key as a string\n\t * @throws Exception\n\t */\n\tpublic String exportKey(String keyAlias, String keyStorePassword) throws Exception\n\t{\n\t\tKey key = this.keyStore.getKey(keyAlias, keyStorePassword.toCharArray());\n\t\treturn new String(Base64.getEncoder().encode(key.getEncoded()));\n\t}\n\t\n\t/**\n\t * Import a provided key into a KeyStore\n\t * @param location - the keystore location\n\t * @param encodedKey - the encoded key to import\n\t * @param keyAlias - the key alias\n\t * @param password - the key store password\n\t * @throws Exception\n\t */\n\tstatic public void importKey(String location, String encodedKey, String keyAlias, String password) throws Exception\n\t{\n\t\tKeyStore store = KeyStore.getInstance(\"JCEKS\");\n\t\tFileInputStream fis = new FileInputStream(location);\n\t\tstore.load(fis, password.toCharArray());\n\t\tfis.close();\n\t\t\n\t\tbyte[] ba = Base64.getDecoder().decode(encodedKey);\n\t\tSecretKey key = new SecretKeySpec(ba,\"AES\");\n\t\tKeyStore.SecretKeyEntry skEntry =\n\t        new KeyStore.SecretKeyEntry(key);\n\t\tstore.setEntry(keyAlias, skEntry, \n\t        new KeyStore.PasswordProtection(password.toCharArray()));\n\t\tFileOutputStream fos = new FileOutputStream(location);\n\t\tstore.store(fos, password.toCharArray());\n\t\tfos.close();\n\n\t}\n\n\t\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/Errmsg.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.common;\r\n\r\nimport java.util.logging.Logger;\r\n\r\n\r\n/**\r\n * standard error handling for Borg\r\n */\r\npublic class Errmsg {\r\n\t\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\r\n\t/**\r\n\t * console error handler\r\n\t *\r\n\t */\r\n\tprivate static class DefaultErrorHandler implements ErrorHandler {\r\n\t\t\r\n\t\t@Override\r\n\t\tpublic void errmsg(Exception e) {\r\n\r\n\t\t\t// treat a warning differently - just show its text\r\n\t\t\tif (e instanceof Warning) {\r\n\t\t\t\tnotice(e.getMessage());\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tlog.severe(e.toString());\r\n\t\t\te.printStackTrace();\r\n\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void notice(String s) {\r\n\r\n\t\t\tlog.info(s);\r\n\t\t\treturn;\r\n\r\n\t\t}\r\n\t}\r\n\r\n\t// initialize to only send errors to the console\r\n\tprivate static ErrorHandler errorHandler = new DefaultErrorHandler();\r\n\r\n\tpublic static ErrorHandler getErrorHandler() {\r\n\t\treturn errorHandler;\r\n\t}\r\n\r\n\tpublic static void setErrorHandler(ErrorHandler errorHandler) {\r\n\t\tErrmsg.errorHandler = errorHandler;\r\n\t}\r\n\t\r\n\tpublic static void logError(Exception e)\r\n\t{\r\n\t\tlog.severe(e.toString());\r\n\t\te.printStackTrace();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/ErrorHandler.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.common;\r\n\r\n/**\r\n * error handler interface\r\n */\r\npublic interface ErrorHandler {\r\n\r\n\r\n\t/**\r\n\t * Output an exception to the user.\r\n\t * \r\n\t * @param e\r\n\t *            the e\r\n\t */\r\n    void errmsg(Exception e);\r\n    \r\n\t/**\r\n\t * output a notice/warning - just shows text\r\n\t * \r\n\t * @param s\r\n\t *            the text to show\r\n\t */\r\n    void notice(String s);\r\n   \r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/IOHelper.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2004 by Mohan Embar - http://www.thisiscool.com/\r\n */\r\n\r\npackage net.sf.borg.common;\r\n\r\nimport java.io.File;\r\nimport java.io.FileOutputStream;\r\nimport java.io.OutputStream;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * standard routines for file I/O with prompting\r\n */\r\npublic class IOHelper {\r\n\r\n\tstatic final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t/**\r\n\t * The home directory; gets updated to ensure it is the last used directory,\r\n\t * initialized as default\r\n\t */\r\n\tstatic private File homeDirectory = new File(\".\");\r\n\r\n\t/**\r\n\t * Gets the home directory\r\n\t * \r\n\t * @return the directory\r\n\t */\r\n\tpublic static File getHomeDirectory() {\r\n\t\treturn (homeDirectory);\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the home directory.\r\n\t */\r\n\tpublic static void setHomeDirectory(String newHome) {\r\n\t\thomeDirectory = new File(newHome);\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * create an output stream to a file, creating parent dirs as needed\r\n\t * \r\n\t * @param file\r\n\t *            the file\r\n\t * \r\n\t * @return the output stream\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic static OutputStream createOutputStream(String file) throws Exception {\r\n\t\tFile fil = new File(file);\r\n\t\tfil.getParentFile().mkdirs();\r\n\t\t// create the containing directory if it doesn't\r\n\t\t// already exist.\r\n\t\treturn new FileOutputStream(fil);\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/LogViewer.java",
    "content": "/*\n * written by google gemini\n */\npackage net.sf.borg.common;\n\nimport javax.swing.*;\nimport javax.swing.table.DefaultTableCellRenderer;\nimport javax.swing.table.DefaultTableModel;\nimport java.awt.*;\nimport java.util.List;\n\npublic class LogViewer extends JFrame {\n\t\n\tprivate static final long serialVersionUID = -6270066756808515613L;\n\n\tprivate class LogRowRenderer extends DefaultTableCellRenderer {\n\t    private static final long serialVersionUID = -3073125084127463837L;\n\n\t\t@Override\n\t    public Component getTableCellRendererComponent(JTable table, Object value, \n\t            boolean isSelected, boolean hasFocus, int row, int column) {\n\t        \n\t        Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\n\n\t        // Get the log type from the first column (index 0)\n\t        String type = table.getValueAt(row, 0).toString();\n\n\t        if (isSelected) {\n\t            c.setBackground(table.getSelectionBackground());\n\t            c.setForeground(table.getSelectionForeground());\n\t        } else {\n\t            // Apply colors based on type\n\t            switch (type) {\n\t                case \"ERROR\" -> {\n\t                    c.setBackground(new Color(255, 210, 210)); // Soft Red\n\t                    c.setForeground(Color.RED.darker());\n\t                }\n\t                case \"UPDATE\" -> {\n\t                    c.setBackground(new Color(210, 255, 210)); // Soft Green\n\t                    c.setForeground(Color.GREEN.darker());\n\t                }\n\t                case \"WARN\" -> {\n\t                    c.setBackground(new Color(255, 245, 200)); // Soft Yellow\n\t                    c.setForeground(new Color(150, 100, 0));  // Dark Orange/Brown\n\t                }\n\t                case \"INFO\" -> {\n\t                    c.setBackground(Color.WHITE);\n\t                    c.setForeground(Color.BLACK);\n\t                }\n\t                default -> {\n\t                    c.setBackground(Color.WHITE);\n\t                    c.setForeground(Color.BLACK);\n\t                }\n\t            }\n\t        }\n\t        return c;\n\t    }\n\t}\n\t\n\tpublic static class LogEntry {\n\t\tpublic final static String INFO = \"INFO\";\n\t\tpublic final static String UPDATE = \"UPDATE\";\n\t\tpublic final static String WARN = \"WARN\";\n\t\tpublic final static String ERROR = \"ERROR\";\n\t\tprivate String type; // INFO, WARN, ERROR\n\t    private String message;\n\n\t    public LogEntry(String type, String message) {\n\t        this.type = type;\n\t        this.message = message;\n\t    }\n\n\t    public String getType() { return type; }\n\t    public String getMessage() { return message; }\n\t}\n\n    public LogViewer(List<LogEntry> logs) {\n        setTitle(\"Log Viewer\");\n        setSize(600, 400);\n        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);\n        //setLocationRelativeTo(null);\n\n        // 1. Define Table Columns\n        String[] columnNames = {\"Type\", \"Description (Click for details)\"};\n        \n        // 2. Load data into the Table Model\n        DefaultTableModel model = new DefaultTableModel(columnNames, 0) {\n            private static final long serialVersionUID = 1L;\n\n\t\t\t@Override\n            public boolean isCellEditable(int row, int column) {\n                return false; // Make table read-only\n            }\n        };\n\n        for (LogEntry log : logs) {\n            model.addRow(new Object[]{log.getType(), log.getMessage()});\n        }\n\n        // 3. Setup JTable and JScrollPane\n        JTable table = new JTable(model);\n        LogRowRenderer renderer = new LogRowRenderer();\n        for (int i = 0; i < table.getColumnCount(); i++) {\n            table.getColumnModel().getColumn(i).setCellRenderer(renderer);\n        }\n        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n        JScrollPane scrollPane = new JScrollPane(table);\n        add(scrollPane, BorderLayout.CENTER);\n\n        // 4. Add Selection Listener for the Detail Window\n        table.getSelectionModel().addListSelectionListener(e -> {\n            if (!e.getValueIsAdjusting()) { // Ensure event only fires once\n                int selectedRow = table.getSelectedRow();\n                if (selectedRow != -1) {\n                    showDetailWindow(logs.get(selectedRow));\n                }\n            }\n        });\n        \n       \n    }\n\n    private void showDetailWindow(LogEntry log) {\n        JFrame detailFrame = new JFrame(\"Log Detail: \" + log.getType());\n        detailFrame.setSize(400, 300);\n        \n        JTextArea textArea = new JTextArea(log.getMessage());\n        textArea.setLineWrap(true);\n        textArea.setWrapStyleWord(true);\n        textArea.setEditable(false);\n        textArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));\n\n        detailFrame.add(new JScrollPane(textArea));\n        detailFrame.setVisible(true);\n        //detailFrame.setLocationRelativeTo(this);\n    }\n\n    public static void main(String[] args) {\n        // Sample Data\n        List<LogEntry> sampleLogs = List.of(\n            new LogEntry(\"INFO\", \"System started successfully at 09:00 AM.\"),\n            new LogEntry(\"WARN\", \"Memory usage exceeding 80% threshold.\"),\n            new LogEntry(\"ERROR\", \"NullPointerException in DatabaseConnector.java:42. Connection failed.\"),\n            new LogEntry(\"UPDATE\", \"User 'Admin' logged in from IP 192.168.1.1.\")\n        );\n\n        SwingUtilities.invokeLater(() -> {\n            new LogViewer(sampleLogs).setVisible(true);\n        });\n    }\n}"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/ModalMessage.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n\n\npackage net.sf.borg.common;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * modal dialog with a scrollable message.\n * The ok button can start as disabled so that the user must wait until it is enabled\n * by the program while the program is doing something important\n */\npublic class ModalMessage extends JDialog {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\t/** The message scroll. */\n\tprivate JScrollPane messageScroll = null;\n\n\t/** The message text. */\n\tprivate JTextArea messageText = null;\n\n\t/** The ok button. */\n\tprivate JButton okButton = null;\n\n\t/**\n\t * Instantiates a new modal message.\n\t * \n\t * @param s the message\n\t * @param enabled if true, enable ok button, otheriwse disable\n\t */\n\tpublic ModalMessage(String s, boolean enabled) {\n\t\tinitComponents();\n\t\tmessageText.setText(s);\n\t\tokButton.setEnabled(enabled);\n\t\tsetModal(true);\n\t}\n\n\t/**\n\t * Append text to the message while. Normally used when the program is continuing to produce output\n\t * for the user and the ok button is disabled. Text is appended on a new line.\n\t * \n\t * @param s the string to append\n\t */\n\tpublic void appendText(String s) {\n\t\tString t = messageText.getText();\n\t\tt += \"\\n\" + s;\n\t\tmessageText.setText(t);\n\t}\n\n\t/**\n\t * Inits the components.\n\t */\n\tprivate void initComponents() {\n\t\tsetDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\n\t\tsetTitle(\"BORG\");\n\t\tthis.setSize(165, 300);\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\n\t\tmessageScroll = new JScrollPane();\n\t\tmessageScroll.setPreferredSize(new java.awt.Dimension(600, 200));\n\n\t\tmessageText = new JTextArea();\n\t\tmessageText.setEditable(false);\n\t\tmessageText.setLineWrap(true);\n\n\t\tmessageScroll.setViewportView(messageText);\n\n\t\tGridBagConstraints gbc = new GridBagConstraints();\n\t\tgbc.gridx = 0;\n\t\tgbc.gridy = 0;\n\t\tgbc.insets = new Insets(4, 4, 4, 4);\n\t\tgbc.weightx = 1.0;\n\t\tgbc.weighty = 1.0;\n\t\tgbc.fill = GridBagConstraints.BOTH;\n\t\ttopPanel.add(messageScroll, gbc);\n\n\t\tJPanel buttonPanel = new JPanel();\n\n\t\tokButton = new JButton();\n\t\tokButton.setText(Resource.getResourceString(\"OK\"));\n\t\tokButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(okButton, null);\n\n\t\tGridBagConstraints gbc2 = new GridBagConstraints();\n\t\tgbc2.gridx = 0;\n\t\tgbc2.gridy = 1;\n\t\tgbc2.insets = new Insets(4, 4, 4, 4);\n\t\tgbc2.weightx = 0.0;\n\t\tgbc2.weighty = 0.0;\n\t\tgbc2.fill = GridBagConstraints.BOTH;\n\t\ttopPanel.add(buttonPanel, gbc2);\n\n\t\tthis.setContentPane(topPanel);\n\n\t\tpack();\n\n\t\tDimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n\t\tDimension labelSize = messageScroll.getPreferredSize();\n\t\tsetLocation(screenSize.width / 2 - (labelSize.width / 2),\n\t\t\t\tscreenSize.height / 2 - (labelSize.height / 2));\n\t}\n\n\t/* (non-Javadoc)\n\t * @see java.awt.Component#setEnabled(boolean)\n\t */\n\t@Override\n\tpublic void setEnabled(boolean e) {\n\t\tokButton.setEnabled(e);\n\t}\n\n\t/**\n\t * Sets the text.\n\t * \n\t * @param s the new text\n\t */\n\tpublic void setText(String s) {\n\t\tmessageText.setText(s);\n\t}\n} \n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/ModalMessageServer.java",
    "content": "package net.sf.borg.common;\n\nimport javax.swing.*;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\n\npublic class ModalMessageServer {\n\n    static volatile private ModalMessageServer singleton = null;\n\n    static public ModalMessageServer getReference() {\n        if (singleton == null) {\n            ModalMessageServer b = new ModalMessageServer();\n            singleton = b;\n        }\n        return (singleton);\n    }\n\n    private ModalMessage modalMessage = null;\n\n    private BlockingQueue<String> messageQ = new LinkedBlockingQueue<>();\n\n    private ModalMessageServer(){\n        Thread t = new Thread(){\n\n            @Override\n            public void run() {\n                try {\n                    while (true) {\n                        String msg = messageQ.take();\n                        if (msg.startsWith(\"lock:\")) {\n                            final String lockmsg = msg.substring(5);\n                            SwingUtilities.invokeLater(new Runnable() {\n                                @Override\n                                public void run() {\n                                    if (modalMessage == null || !modalMessage.isShowing()) {\n                                        modalMessage = new ModalMessage(lockmsg, false);\n                                        modalMessage.setVisible(true);\n                                    } else {\n                                        modalMessage.appendText(lockmsg);\n                                    }\n                                    modalMessage.setEnabled(false);\n                                    modalMessage.toFront();\n                                }\n                            });\n\n                        } else if (msg.startsWith(\"log:\")) {\n                            final String lockmsg = msg.substring(4);\n                            SwingUtilities.invokeLater(new Runnable() {\n                                @Override\n                                public void run() {\n                                    if (modalMessage != null && modalMessage.isShowing()) {\n                                        modalMessage.appendText(lockmsg);\n                                        // modalMessage.setEnabled(false);\n                                        // modalMessage.toFront();\n                                    }\n\n                                }\n                            });\n\n                        } else if (msg.equals(\"unlock\")) {\n                            SwingUtilities.invokeLater(new Runnable() {\n                                @Override\n                                public void run() {\n                                    if (modalMessage.isShowing()) {\n                                        modalMessage.setEnabled(true);\n                                    }\n                                }\n                            });\n\n                        }\n                    }\n                } catch (InterruptedException e) {\n                    Thread.currentThread().interrupt();\n                }\n            }\n        };\n\n        t.start();\n    }\n\n    public void sendMessage(String msg){\n        messageQ.add(msg);\n    }\n\n    public void sendLogMessage(String msg) {\n        sendMessage(\"log:\" + msg);\n    }\n\n}\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/PrefName.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.common;\r\n\r\nimport java.util.Calendar;\r\n\r\n/**\r\n * PrefName contains all of the Borg preference definitions and default values.\r\n * It enforces compile time checking of preference names\r\n */\r\npublic class PrefName {\r\n\r\n\r\n\t/** preference name */\r\n\tprivate String name_;\r\n\r\n\t/** default value */\r\n\tprivate Object default_;\r\n\r\n\t/**\r\n\t * Instantiates a new pref name.\r\n\t * \r\n\t * @param name\r\n\t *            the name\r\n\t * @param def\r\n\t *            the default value\r\n\t */\r\n\tpublic PrefName(String name, Object def) {\r\n\t\tsetName(name);\r\n\t\tsetDefault(def);\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the name.\r\n\t * \r\n\t * @param name_\r\n\t *            the new name\r\n\t */\r\n\tvoid setName(String name_) {\r\n\t\tthis.name_ = name_;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the name.\r\n\t * \r\n\t * @return the name\r\n\t */\r\n\tpublic String getName() {\r\n\t\treturn name_;\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the default value\r\n\t * \r\n\t * @param default_\r\n\t *            the new default\r\n\t */\r\n\tvoid setDefault(Object default_) {\r\n\t\tthis.default_ = default_;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the default value\r\n\t * \r\n\t * @return the default\r\n\t */\r\n\tpublic Object getDefault() {\r\n\t\treturn default_;\r\n\t}\r\n\r\n\t// database related prefs\r\n\t/** database type. */\r\n\tstatic public PrefName DBTYPE = new PrefName(\"dbtype\", \"h2\");\r\n\r\n\tstatic public PrefName JDBCURL = new PrefName(\"jdbcurl\", \"\");\r\n\r\n\t\r\n\t/** h2 database directory */\r\n\tstatic public PrefName H2DIR = new PrefName(\"h2dir\", \"not-set\");\r\n\t\r\n\tstatic public PrefName SQLITEDIR = new PrefName(\"sqlitedir\", \"not-set\");\r\n\r\n\t// misc\r\n\t/** show a stack trace button on error dialogs */\r\n\tstatic public PrefName STACKTRACE = new PrefName(\"stacktrace\", \"false\");\r\n\r\n\t/** show the spash window */\r\n\tstatic public PrefName SPLASH = new PrefName(\"splash\", \"true\");\r\n\r\n\t/** port for the socket listener */\r\n\tstatic public PrefName SOCKETPORT = new PrefName(\"socketport\", Integer.valueOf(2929));\r\n\r\n\t/** start as iconified to system tray */\r\n\tstatic public PrefName BACKGSTART = new PrefName(\"backgstart\", \"false\");\r\n\r\n\t/** use system tray */\r\n\tstatic public PrefName USESYSTRAY = new PrefName(\"useSysTray\", \"true\");\r\n\t\r\n\t\r\n\r\n\t/** show date is system tray */\r\n\tstatic public PrefName SYSTRAYDATE = new PrefName(\"sysTrayDate\", \"true\");\r\n\r\n\t/** backup directory for auto backup */\r\n\tstatic public PrefName BACKUPDIR = new PrefName(\"backupDir\", \"\");\r\n\r\n\t/** print in color */\r\n\tstatic public PrefName COLORPRINT = new PrefName(\"colorprint\", \"false\");\r\n\r\n\t/** show public appointments */\r\n\t//static public PrefName SHOWPUBLIC = new PrefName(\"showpublic\", \"true\");\r\n\r\n\t/** show private appointments */\r\n\tstatic public PrefName SHOWPRIVATE = new PrefName(\"showprivate\", \"false\");\r\n\r\n\t/** show us holidays */\r\n\tstatic public PrefName SHOWUSHOLIDAYS = new PrefName(\"show_us_holidays\", \"true\");\r\n\r\n\t/** show canadian holidays */\r\n\tstatic public PrefName SHOWCANHOLIDAYS = new PrefName(\"show_can_holidays\", \"false\");\r\n\r\n\t/** sort appointments by priority for a day */\r\n\tstatic public PrefName PRIORITY_SORT = new PrefName(\"priority_sort\", \"false\");\r\n\r\n\t/** the first day of the week */\r\n\tstatic public PrefName FIRSTDOW = new PrefName(\"first_dow\", Integer.valueOf(Calendar.SUNDAY));\r\n\r\n\t/** show military time */\r\n\tstatic public PrefName MILTIME = new PrefName(\"miltime\", \"false\");\r\n\r\n\t/** earliest hour in week grid */\r\n\tstatic public PrefName WKSTARTHOUR = new PrefName(\"wkStartHour\", \"7\");\r\n\r\n\t/** latest hour in week grid */\r\n\tstatic public PrefName WKENDHOUR = new PrefName(\"wkEndHour\", \"22\");\r\n\r\n\t/** contents of the default appointment in XML */\r\n\tstatic public PrefName DEFAULT_APPT = new PrefName(\"defaultAppt\", \"\");\r\n\r\n\t/** show the day of the year */\r\n\tstatic public PrefName DAYOFYEAR = new PrefName(\"showDayOfYear\", \"false\");\r\n\r\n\t/** truncate appointment text after 1 line */\r\n\tstatic public PrefName TRUNCAPPT = new PrefName(\"truncate_appt\", \"true\");\r\n\r\n\t/** use iso week numbering */\r\n\tstatic public PrefName ISOWKNUMBER = new PrefName(\"isowknumber\", \"true\");\r\n\r\n\t/** do not show strikethrough appointments */\r\n\tstatic public PrefName HIDESTRIKETHROUGH = new PrefName(\"hide_strike\", \"false\");\r\n\r\n\t/** show the entire undo stack (debugging) */\r\n\tstatic public PrefName SHOW_UNDO_STACK = new PrefName(\"show_undo_stack\", \"false\");\r\n\r\n\tstatic public PrefName REMINDERS = new PrefName(\"reminders\", \"true\");\r\n\r\n\tstatic public PrefName BEEPINGREMINDERS = new PrefName(\"beeping_reminders\", \"true\");\r\n\r\n\t/** how often tp pop up reminders for untimed todos */\r\n\tstatic public PrefName TODOREMINDERMINS = new PrefName(\"todo_reminder_mins\", Integer.valueOf(30));\r\n\r\n\t/** option to consolidate all reminders in a single list window */\r\n\tstatic public PrefName REMINDERLIST = new PrefName(\"reminder_list\", \"true\");\r\n\r\n\t/** show reminders for tasks */\r\n\tstatic public PrefName TASKREMINDERS = new PrefName(\"task_reminders\", \"true\");\r\n\r\n\t/** days before a birthday to show birthday reminders */\r\n\tstatic public PrefName BIRTHDAYREMINDERDAYS = new PrefName(\"bd_reminder_days\", Integer.valueOf(7));\r\n\t\r\n\t//static public PrefName TASKBAR_REMINDERS = new PrefName(\"taskbar_reminders\", \"false\");\r\n\r\n\tstatic public PrefName EMAILENABLED = new PrefName(\"email_enabled\", \"false\");\r\n\tpublic static PrefName DAILYEMAILENABLED = new PrefName(\"daily_email_enabled\", \"true\");;\r\n\tstatic public PrefName EMAILSERVER = new PrefName(\"email_server\", \"\");\r\n\tstatic public PrefName EMAILADDR = new PrefName(\"email_addr\", \"\");\r\n\tstatic public PrefName EMAILFROM = new PrefName(\"email_from\", \"\");\r\n\tstatic public PrefName EMAILLAST = new PrefName(\"email_last\", Integer.valueOf(0));\r\n\tstatic public PrefName EMAILDEBUG = new PrefName(\"email_debug\", \"0\");\r\n\tstatic public PrefName EMAILTIME = new PrefName(\"email_time\", Integer.valueOf(0));\r\n\tstatic public PrefName EMAILUSER = new PrefName(\"email_user\", \"\");\r\n\tstatic public PrefName EMAILPASS = new PrefName(\"email_pass\", \"\");\r\n\tstatic public PrefName EMAILPORT = new PrefName(\"email_port\", \"25\");\r\n\tstatic public PrefName ENABLETLS = new PrefName(\"enable_tls\", \"false\");\r\n\r\n\tstatic public PrefName REMMINS = new PrefName(\"remmins\",\r\n\t\t\t\"-10,-5,0,1,2,3,4,5,10,15,20,30,45,60,90,120,180,240,300,360\");\r\n\r\n\tstatic public PrefName DEFFONT = new PrefName(\"defaultfont\", \"\");\r\n\tstatic public PrefName APPTFONT = new PrefName(\"apptfont\", \"SansSerif-10\");\r\n\tstatic public PrefName DAYVIEWFONT = new PrefName(\"dayviewfont\", \"SansSerif-10\");\r\n\tstatic public PrefName WEEKVIEWFONT = new PrefName(\"weekviewfont\", \"SansSerif-10\");\r\n\tstatic public PrefName PRINTFONT = new PrefName(\"monthviewfont\", \"SansSerif-6\");\r\n\tstatic public PrefName YEARVIEWFONT = new PrefName(\"yearviewfont\", \"SansSerif-7\");\r\n\tstatic public PrefName TRAYFONT = new PrefName(\"trayfont\", \"SansSerif-BOLD-12\");\r\n\t\r\n\tstatic public PrefName ICONSIZE = new PrefName(\"icon_size\", Integer.valueOf(16));\r\n\r\n\r\n\tstatic public PrefName LNF = new PrefName(\"lnf\", \"com.formdev.flatlaf.FlatLightLaf\");\r\n\r\n\tstatic public PrefName COUNTRY = new PrefName(\"country\", \"\");\r\n\tstatic public PrefName LANGUAGE = new PrefName(\"language\", \"\");\r\n\r\n\t/** use user colors on todo view */\r\n\tstatic public PrefName UCS_ONTODO = new PrefName(\"ucs_ontodo\", \"false\");\r\n\r\n\t/** mark todos on the calendar */\r\n\tstatic public PrefName UCS_MARKTODO = new PrefName(\"ucs_marktodo\", \"true\");\r\n\r\n\t/** characters or image to mark todos with */\r\n\tstatic public PrefName UCS_MARKER = new PrefName(\"ucs_marker\", \"redball.gif\");\r\n\r\n\t/** draw gradient color in appointment boxes - can slow down older machines */\r\n\tstatic public PrefName GRADIENT_APPTS = new PrefName(\"gradient_appts\", \"true\");\r\n\r\n\t/** when showing tasks project and subtasks, prepend a prefix and id number */\r\n\tstatic public PrefName TASK_SHOW_ABBREV = new PrefName(\"task_show_abbrev\", \"false\");\r\n\r\n\t/** show tasks on calendar */\r\n\tstatic public PrefName CAL_SHOW_TASKS = new PrefName(\"cal_show_tasks\", \"true\");\r\n\r\n\t/** show subtasks on calendar */\r\n\tstatic public PrefName CAL_SHOW_SUBTASKS = new PrefName(\"cal_show_subtasks\", \"true\");\r\n\r\n\t/** show task number and status in task tree */\r\n\tstatic public PrefName TASK_TREE_SHOW_STATUS = new PrefName(\"task_tree_show_status\", \"false\");\r\n\r\n\t// days left until due for each color\r\n\tstatic public PrefName RED_DAYS = new PrefName(\"red_days\", Integer.valueOf(2));\r\n\tstatic public PrefName ORANGE_DAYS = new PrefName(\"orange_days\", Integer.valueOf(7));\r\n\tstatic public PrefName YELLOW_DAYS = new PrefName(\"yellow_days\", Integer.valueOf(14));\r\n\r\n\t/** keystore location */\r\n\tstatic public PrefName KEYSTORE = new PrefName(\"key_store\", \"\");\r\n\r\n\t/** cached password time to live in seconds */\r\n\tstatic public PrefName PASSWORD_TTL = new PrefName(\"pw_ttl\", Integer.valueOf(300));\r\n\r\n\t/** todo quick add, clear text after add */\r\n\tstatic public PrefName TODO_QUICK_ENTRY_AUTO_CLEAR_TEXT_FIELD = new PrefName(\r\n\t\t\t\"todo_option_auto_clear_text\", \"false\");\r\n\r\n\t/** todo quick add, default date to today */\r\n\tstatic public PrefName TODO_QUICK_ENTRY_AUTO_SET_DATE_FIELD = new PrefName(\r\n\t\t\t\"todo_option_auto_date_today\", \"false\");\r\n\t\r\n\t/** show only current todo in a repeating todo */\r\n\tstatic public PrefName TODO_ONLY_SHOW_CURRENT = new PrefName(\"todo_only_show_current\", \"false\");\r\n\t\r\n\t/** shutdown action */\r\n\tstatic public PrefName SHUTDOWN_ACTION = new PrefName(\"shutdown_action\", \"\");\r\n\tpublic static final PrefName SHUTDOWNTIME = new PrefName(\"shuttime\", \"0\");\r\n\r\n\t/** debug flag - trigger debug logging */\r\n\tpublic static final PrefName DEBUG = new PrefName(\"debug\", \"false\");\r\n\tpublic static final PrefName AUDITLOG = new PrefName(\"auditlog\", \"false\");\r\n\r\n\t// limit on the max text size that can be put into a text area to prevent\r\n\t// memory issues\r\n\tpublic static final PrefName MAX_TEXT_SIZE = new PrefName(\"max_text_size\", Integer.valueOf(1024 * 1024));\r\n\r\n\tpublic static PrefName ICAL_EXPORTYEARS = new PrefName(\"ical-export-years\", Integer.valueOf(2));\r\n\r\n\t// option to export todos as VTODO objects instead of VEVENTS\r\n\tpublic static PrefName ICAL_EXPORT_TODO = new PrefName(\"ical-export-todo\", \"false\");\r\n\r\n\tpublic static final PrefName GOOGLE_SYNC = new PrefName(\"google_sync\", \"false\");\r\n\tpublic static final PrefName GCAL_CAL_ID = new PrefName(\"google_cal_id\", \"\");\r\n\tpublic static final PrefName GCAL_TODO_CAL_ID = new PrefName(\"google_todo_cal_id\", \"\");\r\n\tpublic static final PrefName GCAL_TASKLIST_ID = new PrefName(\"google_tasklist_id\", \"\");\r\n\tpublic static final PrefName GOOGLE_CRED_FILE = new PrefName(\"google_cred_file\", \"\");\r\n\tpublic static final PrefName GOOGLE_TOKEN_DIR = new PrefName(\"google_token_dir\", \"\");\r\n\tpublic static final PrefName GCAL_EXPORTYEARS = new PrefName(\"gcal-export-years\", Integer.valueOf(2));\r\n\tpublic static final PrefName GOOGLE_SUBSCRIBED = new PrefName(\"google_subscribed\", \"\");\r\n\tpublic static final PrefName GOOGLE_DB_FILE_PATH = new PrefName(\"google_db_file_path\", \"\");\r\n\tpublic static final PrefName SUB_CACHE_FILENAME = new PrefName(\"sub_cache_file_name\", \".borg.subcache\");\r\n\tpublic static final PrefName GOOGLE_DB_FORCE_DOWNLOAD = new PrefName(\"google_db_force_dl\", \"false\");\r\n\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/Prefs.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.common;\r\n\r\nimport java.io.FileInputStream;\r\nimport java.io.InputStream;\r\nimport java.io.OutputStream;\r\nimport java.util.ArrayList;\r\nimport java.util.logging.Logger;\r\nimport java.util.prefs.Preferences;\r\n\r\n\r\n/**\r\n * class for managing Borg preferences.\r\n */\r\npublic class Prefs {\r\n\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\tstatic private String prefRootNode = \"net/sf/borg/common/util\";\r\n\r\n\t/**\r\n\t * Interface for classes that want to be notified of preference changes\r\n\t */\r\n\tpublic interface Listener {\r\n\t\t\r\n\t\t/**called when preferences changed.\r\n\t\t */\r\n\t\tvoid prefsChanged();\r\n\t}\r\n\r\n\t/** list of listeners */\r\n\tstatic private final ArrayList<Listener> listeners = new ArrayList<Listener>();\r\n\r\n\t/**\r\n\t * add a listener\r\n\t * \r\n\t * @param listener the listener\r\n\t */\r\n\tstatic public void addListener(Listener listener) {\r\n\t\tlisteners.add(listener);\r\n\t}\r\n\r\n\t/**\r\n\t * Notify listeners of a pref change.\r\n\t */\r\n\tstatic public void notifyListeners() {\r\n\t\tfor (int i = 0; i < listeners.size(); i++) {\r\n\t\t\tListener v = listeners.get(i);\r\n\t\t\tv.prefsChanged();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Get a string preference value\r\n\t * \r\n\t * @param pn the preference name object\r\n\t * \r\n\t * @return the value\r\n\t */\r\n\tpublic static String getPref(PrefName pn) {\r\n\t\tObject o = getPrefObject(pn);\r\n\t\tif (o instanceof Integer)\r\n\t\t\treturn Integer.toString(((Integer) o).intValue());\r\n\t\treturn (String) getPrefObject(pn);\r\n\t}\r\n\r\n\t/**\r\n\t * Get an integer preference value\r\n\t * \r\n\t * @param pn the preference name object\r\n\t * \r\n\t * @return the int value\r\n\t */\r\n\tpublic static int getIntPref(PrefName pn) {\r\n\t\treturn (((Integer) Prefs.getPrefObject(pn)).intValue());\r\n\t}\r\n\r\n\t/**\r\n\t * Get a boolean preference value\r\n\t * \r\n\t * @param pn the preference name object\r\n\t * \r\n\t * @return the boolen value\r\n\t */\r\n\tpublic static boolean getBoolPref(PrefName pn) {\r\n\t\tString s = getPref(pn);\r\n\t\treturn s != null && s.equals(\"true\");\r\n\t}\r\n\r\n\t/**\r\n\t * Get an Object preference value\r\n\t * \r\n\t * @param pn the the preference name object\r\n\t * \r\n\t * @return the Object value\r\n\t */\r\n\tprivate static Object getPrefObject(PrefName pn) {\r\n\t\tPreferences prefs = getPrefNode();\r\n\t\tif (pn.getDefault() instanceof Integer) {\r\n\t\t\tint val = prefs.getInt(pn.getName(), ((Integer) pn.getDefault()).intValue());\r\n\t\t\treturn (Integer.valueOf(val));\r\n\t\t}\r\n\r\n\t\tString val = prefs.get(pn.getName(), (String) pn.getDefault());\r\n\t\treturn (val);\r\n\t}\r\n\r\n\t/**\r\n\t * store a preference\r\n\t * \r\n\t * @param pn the preference name object \r\n\t * @param val the value\r\n\t */\r\n\tpublic static void putPref(PrefName pn, Object val) {\r\n\r\n\t\tlog.fine(\"putpref-\" + pn.getName() + \"-\" + val);\r\n\t\tPreferences prefs = getPrefNode();\r\n\t\tif (pn.getDefault() instanceof Integer) {\r\n\t\t\tif (val instanceof Integer) {\r\n\t\t\t\tprefs.putInt(pn.getName(), ((Integer) val).intValue());\r\n\t\t\t} else {\r\n\t\t\t\tprefs.putInt(pn.getName(), Integer.parseInt((String) val));\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tprefs.put(pn.getName(), (String) val);\r\n\t\t}\r\n\t}\r\n\t\r\n\tpublic static void delPref(PrefName pn) {\r\n\t\tPreferences prefs = getPrefNode();\r\n\t\tprefs.remove(pn.getName());\r\n\t}\r\n\r\n\t/**\r\n\t * Get the java.util.prefs.Preferences node where borg stores preferences.\r\n\t * The path is now hard coded so that it does not change when borg is refactored\r\n\t * \r\n\t * @return the Preferences node\r\n\t */\r\n\tstatic private Preferences getPrefNode() {\r\n\t\t// hard code to original prefs location for backward compatiblity\r\n\t\tPreferences root = Preferences.userRoot();\r\n\t\treturn root.node(prefRootNode);\r\n\t}\r\n\r\n\t/**\r\n\t * constructor\r\n\t */\r\n\tprivate Prefs() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * Import preferences from a file\r\n\t * \r\n\t * @param filename the filename\r\n\t * @throws Exception \r\n\t */\r\n\tpublic static void importPrefs(String filename) throws Exception {\r\n\r\n\t\tInputStream istr = new FileInputStream(filename);\r\n\t\tPreferences.importPreferences(istr);\r\n\t\tistr.close();\r\n\t}\r\n\r\n\t/**\r\n\t * Export preferences to a file\r\n\t * \r\n\t * @param filename the filename\r\n\t */\r\n\tpublic static void export(String filename) {\r\n\t\ttry {\r\n\t\t\tOutputStream oostr = IOHelper.createOutputStream(filename);\r\n\t\t\tPreferences prefs = getPrefNode();\r\n\t\t\tprefs.exportNode(oostr);\r\n\t\t\toostr.close();\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void setPrefRootNode(String prefRootNode) {\r\n\t\tPrefs.prefRootNode = prefRootNode;\r\n\t}\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/PrintHelper.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.common;\r\n\r\nimport java.awt.print.PageFormat;\r\nimport java.awt.print.Paper;\r\nimport java.awt.print.Printable;\r\nimport java.awt.print.PrinterJob;\r\nimport java.lang.reflect.InvocationTargetException;\r\nimport java.lang.reflect.Method;\r\n\r\n/**\r\n * Some common printing related utilities\r\n */\r\npublic class PrintHelper {\r\n\t\r\n\t/**\r\n\t * Initializes the PrinterJob settings and sets a default print service\r\n\t * \r\n\t * @param job the job\r\n\t */\r\n\tprivate static void initPrinterJobFields(PrinterJob job) {\r\n\t\tjob.setJobName(\"BORG Printout\");\r\n\t\tClass<? extends PrinterJob> klass = job.getClass();\r\n\t\ttry {\r\n\t\t\tClass<?> printServiceClass = Class.forName(\"javax.print.PrintService\");\r\n\t\t\tMethod method = klass.getMethod(\"getPrintService\", (Class[]) null);\r\n\t\t\tObject printService = method.invoke(job, (Object[]) null);\r\n\t\t\tmethod = klass.getMethod(\"setPrintService\",\r\n\t\t\t\t\tprintServiceClass);\r\n\t\t\tmethod.invoke(job, printService);\r\n\t\t} catch (NoSuchMethodException e) {\r\n\t\t  // empty\r\n\t\t} catch (IllegalAccessException e) {\r\n\t\t  // empty\r\n\t\t} catch (InvocationTargetException e) {\r\n\t\t  // empty\r\n\t\t} catch (ClassNotFoundException e) {\r\n\t\t  // empty\r\n\t\t}\r\n\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Prints a printable object. Prompts the user for print settings\r\n\t * using the standard native dialog if available\r\n\t * \r\n\t * @param p the Printable\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tstatic public void printPrintable(Printable p) throws Exception {\r\n\r\n\t\tPrinterJob printJob = PrinterJob.getPrinterJob();\r\n\t\tinitPrinterJobFields(printJob);\r\n\t\t\r\n\t\tPageFormat pageFormat = printJob.defaultPage();\r\n\t\tPaper paper = pageFormat.getPaper();\r\n\t\t//pageFormat.setOrientation(PageFormat.LANDSCAPE);\r\n\t\t//paper.setSize(8.5*72,11*72);\r\n\t\t//paper.setImageableArea(0.875*72,0.625*72,6.75*72,9.75*72);\r\n\t\tpageFormat.setPaper(paper);\r\n\r\n\t\tprintJob.setPrintable(p,pageFormat);\r\n\t\t\r\n\t\tif (printJob.printDialog())\r\n\t\t\tprintJob.print();\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/Resource.java",
    "content": "/*\r\nThis file is part of BORG.\r\n\r\n\tBORG is free software; you can redistribute it and/or modify\r\n\tit under the terms of the GNU General Public License as published by\r\n\tthe Free Software Foundation; either version 2 of the License, or\r\n\t(at your option) any later version.\r\n\r\n\tBORG is distributed in the hope that it will be useful,\r\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n\tGNU General Public License for more details.\r\n\r\n\tYou should have received a copy of the GNU General Public License\r\n\talong with BORG; if not, write to the Free Software\r\n\tFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.common;\r\n\r\nimport java.io.InputStream;\r\nimport java.text.MessageFormat;\r\nimport java.util.MissingResourceException;\r\nimport java.util.Properties;\r\nimport java.util.ResourceBundle;\r\n\r\n/**\r\n * Common logic for dealing with Resource Bundles.\r\n */\r\npublic class Resource {\r\n\r\n\t/** the borg version */\r\n\tprivate static String version_ = null;\r\n\r\n\t/**\r\n\t * Get a resource string from the borg bundle. Translates escaped newlines\r\n\t * to real newlines.\r\n\t * \r\n\t * @param key\r\n\t *            the resource key\r\n\t * \r\n\t * @return the resource string or \"??key??\" if the string is not found\r\n\t */\r\n\tprivate static String getRawResourceString(String key) {\r\n\t\ttry {\r\n\t\t\tString res = ResourceBundle.getBundle(\"borg_resource\").getString(\r\n\t\t\t\t\tkey);\r\n\r\n\t\t\tif (res.indexOf(\"\\\\n\") == -1)\r\n\t\t\t\treturn (res);\r\n\r\n\t\t\tStringBuffer sb = new StringBuffer();\r\n\t\t\tfor (int i = 0; i < res.length(); i++) {\r\n\r\n\t\t\t\tif (res.charAt(i) == '\\\\' && (i < res.length() - 1)\r\n\t\t\t\t\t\t&& res.charAt(i + 1) == 'n') {\r\n\t\t\t\t\ti++;\r\n\t\t\t\t\tsb.append('\\n');\r\n\t\t\t\t} else {\r\n\t\t\t\t\tsb.append(res.charAt(i));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn (sb.toString());\r\n\t\t} catch (MissingResourceException m) {\r\n\t\t\treturn (\"?\" + key + \"?\");\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Get a resource string from the borg resource bundle. Translates escaped\r\n\t * newlines to real newlines. Substitute variables\r\n\t * \r\n\t * @param resourceKey\r\n\t *            the resource key\r\n\t * @param params\r\n\t *            substitutable parameters\r\n\t * \r\n\t * @return the resource string\r\n\t */\r\n\tpublic static String getResourceString(String resourceKey, Object[] params) {\r\n\t\treturn MessageFormat.format(getResourceString(resourceKey), params);\r\n\t}\r\n\r\n\t/**\r\n\t * Get a resource string from the borg resource bundle. Translates escaped\r\n\t * newlines to real newlines.\r\n\t * \r\n\t * @param resourceKey\r\n\t *            the resource key\r\n\t * \r\n\t * @return the resource string\r\n\t */\r\n\tpublic static String getResourceString(String resourceKey) {\r\n\t\treturn parseResourceText(getRawResourceString(resourceKey));\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the borg version, which is stored in its own properties file\r\n\t * \r\n\t * @return the version\r\n\t */\r\n\tpublic static String getVersion() {\r\n\t\tif (version_ == null) {\r\n\t\t\ttry {\r\n\t\t\t\t// get the version and build info from a properties file in the\r\n\t\t\t\t// jar file\r\n\t\t\t\tInputStream is = Resource.class.getResource(\"/properties\")\r\n\t\t\t\t\t\t.openStream();\r\n\t\t\t\tProperties props = new Properties();\r\n\t\t\t\tprops.load(is);\r\n\t\t\t\tis.close();\r\n\t\t\t\tversion_ = props.getProperty(\"borg.version\");\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn (version_);\r\n\t}\r\n\r\n\tprivate static String parseResourceText(String s) {\r\n\t\tint pos;\r\n\t\tif ((pos = s.indexOf('|')) != -1)\r\n\t\t\treturn (s.substring(0, pos));\r\n\t\treturn s;\r\n\t}\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/SendJavaMail.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * Sendmail.java\r\n *\r\n * Created on October 20, 2003, 10:01 AM\r\n */\r\n\r\npackage net.sf.borg.common;\r\n\r\nimport javax.activation.DataHandler;\r\nimport javax.activation.DataSource;\r\nimport javax.activation.FileDataSource;\r\nimport javax.mail.*;\r\nimport javax.mail.internet.InternetAddress;\r\nimport javax.mail.internet.MimeBodyPart;\r\nimport javax.mail.internet.MimeMessage;\r\nimport javax.mail.internet.MimeMultipart;\r\nimport javax.net.ssl.SSLContext;\r\nimport java.io.File;\r\nimport java.util.Date;\r\nimport java.util.Properties;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * utility class to send out email via java mail\r\n */\r\n\r\npublic class SendJavaMail {\r\n\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t/**\r\n\t * boilerplate Authenticator.\r\n\t */\r\n\tprivate static class MyAuthenticator extends Authenticator {\r\n\r\n\t\tprivate final String username;\r\n\t\tprivate final String password;\r\n\r\n\t\tpublic MyAuthenticator(String user, String pass) {\r\n\t\t\tusername = user;\r\n\t\t\tpassword = pass;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic PasswordAuthentication getPasswordAuthentication() {\r\n\t\t\treturn new PasswordAuthentication(username, password);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Send an mail.\r\n\t * \r\n\t * @param host\r\n\t *            the smtp host\r\n\t * @param msgText\r\n\t *            the email text\r\n\t * @param subject\r\n\t *            the subject\r\n\t * @param from\r\n\t *            the from address\r\n\t * @param to\r\n\t *            the to address\r\n\t * @param user\r\n\t *            the smtp user\r\n\t * @param pass\r\n\t *            the smtp password\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic static void sendMail(String host, String msgText, String subject,\r\n\t\t\tString from, String to, String user, String pass) throws Exception {\r\n\t\t\r\n\t\tlog.info(\"Sending mail to: \" + to);\r\n\r\n\t\t//log.info(\"protocols:\" + String.join(\" \", SSLContext.getDefault().getSupportedSSLParameters().getProtocols()));\r\n\r\n\t\t// create some properties and get the default Session\r\n\t\tProperties props = new Properties();\r\n\t\tprops.put(\"mail.smtp.host\", host);\r\n\t\tString port = Prefs.getPref(PrefName.EMAILPORT);\r\n\t\tprops.put(\"mail.smtp.port\", port);\r\n\t\tString ed = Prefs.getPref(PrefName.EMAILDEBUG);\r\n\t\tif (ed.equals(\"1\"))\r\n\t\t\tprops.put(\"mail.debug\", \"true\");\r\n\t\tif (Prefs.getBoolPref(PrefName.ENABLETLS))\r\n\t\t\tprops.put(\"mail.smtp.starttls.enable\", \"true\");\r\n\r\n\t\tString protocols = String.join(\" \",\r\n\t\t\t\tSSLContext\r\n\t\t\t\t\t\t.getDefault()\r\n\t\t\t\t\t\t.getSupportedSSLParameters()\r\n\t\t\t\t\t\t.getProtocols()\r\n\t\t);\r\n\r\n\t\tprops.put(\"mail.smtp.ssl.protocols\", protocols);\r\n\r\n\t\tAuthenticator auth = null;\r\n\t\tif (user != null && !user.equals(\"\") && pass != null\r\n\t\t\t\t&& !pass.equals(\"\")) {\r\n\t\t\tauth = new MyAuthenticator(user, pass);\r\n\t\t\tprops.put(\"mail.smtp.auth\", \"true\");\r\n\t\t}\r\n\t\tSession session = Session.getInstance(props, auth);\r\n\r\n\t\t//session.setDebug(true);\r\n\r\n\t\ttry {\r\n\t\t\t// create a message\r\n\t\t\tMessage msg = new MimeMessage(session);\r\n\t\t\tmsg.setFrom(new InternetAddress(from));\r\n\t\t\tInternetAddress[] address = { new InternetAddress(to) };\r\n\t\t\tmsg.setRecipients(Message.RecipientType.TO, address);\r\n\t\t\t// msg.setSubject(Resource.getResourceString(\"Reminder_Notice\"));\r\n\t\t\tmsg.setSubject(subject);\r\n\t\t\tmsg.setSentDate(new Date());\r\n\t\t\tmsg.setText(msgText);\r\n\r\n\t\t\tTransport.send(msg);\r\n\t\t} catch (MessagingException mex) {\r\n\t\t\tprocessMessagingException(mex);\r\n\t\t\tthrow mex;\r\n\t\t}\r\n\t\t\r\n\t\tlog.info(\"mail sent\");\r\n\t}\r\n\r\n\t/**\r\n\t * Send a multipart mime email with attachments\r\n\t * \r\n\t * @param host\r\n\t *            the smtp host\r\n\t * @param msgText\r\n\t *            the email text\r\n\t * @param subject\r\n\t *            the subject\r\n\t * @param from\r\n\t *            the from address\r\n\t * @param to\r\n\t *            the to address\r\n\t * @param user\r\n\t *            the smtp user\r\n\t * @param pass\r\n\t *            the smtp password\r\n\t * @param attachments\r\n\t * \t\t\t  array of filenames to attach\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic static void sendMailWithAttachments(String host, String msgText,\r\n\t\t\tString subject, String from, String to, String user, String pass, String[] attachments)\r\n\t\t\tthrows Exception {\r\n\r\n\t\t// create some properties and get the default Session\r\n\t\tProperties props = new Properties();\r\n\t\tprops.put(\"mail.smtp.host\", host);\r\n\t\tString port = Prefs.getPref(PrefName.EMAILPORT);\r\n\t\tprops.put(\"mail.smtp.port\", port);\r\n\t\tString ed = Prefs.getPref(PrefName.EMAILDEBUG);\r\n\t\tif (ed.equals(\"1\"))\r\n\t\t\tprops.put(\"mail.debug\", \"true\");\r\n\t\tif (Prefs.getBoolPref(PrefName.ENABLETLS))\r\n\t\t\tprops.put(\"mail.smtp.starttls.enable\", \"true\");\r\n\r\n\t\tAuthenticator auth = null;\r\n\t\tif (user != null && !user.equals(\"\") && pass != null\r\n\t\t\t\t&& !pass.equals(\"\")) {\r\n\t\t\tauth = new MyAuthenticator(user, pass);\r\n\t\t\tprops.put(\"mail.smtp.auth\", \"true\");\r\n\t\t}\r\n\t\tSession session = Session.getInstance(props, auth);\r\n\r\n\t\t//session.setDebug(true);\r\n\r\n\t\ttry {\r\n\t\t\t// create a message\r\n\t\t\tMessage msg = new MimeMessage(session);\r\n\t\t\tmsg.setFrom(new InternetAddress(from));\r\n\t\t\tInternetAddress[] address = { new InternetAddress(to) };\r\n\t\t\tmsg.setRecipients(Message.RecipientType.TO, address);\r\n\t\t\t// msg.setSubject(Resource.getResourceString(\"Reminder_Notice\"));\r\n\t\t\tmsg.setSubject(subject);\r\n\t\t\tmsg.setSentDate(new Date());\r\n\r\n\t\t\tMultipart mp = new MimeMultipart();\r\n\r\n\t\t\tMimeBodyPart textpart = new MimeBodyPart();\r\n\t\t\ttextpart.setText(msgText);\r\n\t\t\tmp.addBodyPart(textpart);\r\n\t\t\t\r\n\t\t\tfor( String filename : attachments)\r\n\t\t\t{\r\n\t\t\t\tFile file = new File(filename);\r\n\t\t\t\tMimeBodyPart attpart = new MimeBodyPart();\r\n\t\t\t    DataSource source = \r\n\t\t\t      new FileDataSource(file);\r\n\t\t\t    attpart.setDataHandler(\r\n\t\t\t      new DataHandler(source));\r\n\t\t\t    attpart.setFileName(file.getName());\r\n\t\t\t    mp.addBodyPart(attpart);\r\n\t\t\t}\r\n\r\n\t\t\t// add the Multipart to the message\r\n\t\t\tmsg.setContent(mp);\r\n\t\t\tTransport.send(msg);\r\n\t\t} catch (MessagingException mex) {\r\n\t\t\tprocessMessagingException(mex);\r\n\t\t\tthrow mex;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Process a messaging exception - print out something useful to stdout\r\n\t * \r\n\t * @param mex\r\n\t *            the MessagingException\r\n\t */\r\n\tstatic private void processMessagingException(MessagingException mex) {\r\n\t\tStringBuffer buf = new StringBuffer();\r\n\t\tbuf.append(\"\\n--Exception handling in BORG.SendJavaMail\\n\");\r\n\r\n\t\tmex.printStackTrace();\r\n\t\tException ex = mex;\r\n\t\tdo {\r\n\t\t\tif (ex instanceof SendFailedException) {\r\n\t\t\t\tSendFailedException sfex = (SendFailedException) ex;\r\n\t\t\t\tAddress[] invalid = sfex.getInvalidAddresses();\r\n\t\t\t\tif (invalid != null) {\r\n\t\t\t\t\tbuf.append(\"    ** Invalid Addresses\\n\");\r\n\r\n\t\t\t\t\tfor (int i = 0; i < invalid.length; i++)\r\n\t\t\t\t\t\tbuf.append(\"         \" + invalid[i] + \"\\n\");\r\n\r\n\t\t\t\t}\r\n\t\t\t\tAddress[] validUnsent = sfex.getValidUnsentAddresses();\r\n\t\t\t\tif (validUnsent != null) {\r\n\t\t\t\t\tbuf.append(\"    ** ValidUnsent Addresses\\n\");\r\n\r\n\t\t\t\t\tfor (int i = 0; i < validUnsent.length; i++)\r\n\t\t\t\t\t\tbuf.append(\"         \" + validUnsent[i] + \"\\n\");\r\n\r\n\t\t\t\t}\r\n\t\t\t\tAddress[] validSent = sfex.getValidSentAddresses();\r\n\t\t\t\tif (validSent != null) {\r\n\t\t\t\t\tbuf.append(\"    ** ValidSent Addresses\\n\");\r\n\r\n\t\t\t\t\tfor (int i = 0; i < validSent.length; i++)\r\n\t\t\t\t\t\tbuf.append(\"         \" + validSent[i] + \"\\n\");\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (ex instanceof MessagingException)\r\n\t\t\t\tex = ((MessagingException) ex).getNextException();\r\n\t\t\telse\r\n\t\t\t\tex = null;\r\n\t\t} while (ex != null);\r\n\t\t\r\n\t\tlog.severe(buf.toString());\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/SocketClient.java",
    "content": "package net.sf.borg.common;\r\n\r\n// This example is from the book _Java in a Nutshell_ by David Flanagan.\r\n// modified by Mike Berger. No license appllies to this source file\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.IOException;\r\nimport java.io.InputStreamReader;\r\nimport java.io.PrintStream;\r\nimport java.net.Socket;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * SocketClient sends text messages over a socket\r\n */\r\n\r\npublic class SocketClient {\r\n\t\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\r\n\t/**\r\n\t * Send a msg.\r\n\t * \r\n\t * @param host the host\r\n\t * @param port the port\r\n\t * @param msg the msg\r\n\t * \r\n\t * @return the response string\r\n\t * \r\n\t * @throws IOException Signals that an I/O exception has occurred.\r\n\t */\r\n\tpublic static String sendMsg(String host, int port, String msg) throws IOException {\r\n\t\tSocket s = null;\r\n\t\tString line = null;\r\n\t\ttry {\r\n\t\t\ts = new Socket(host, port);\r\n\t\t\tBufferedReader sin = new BufferedReader(new InputStreamReader(s\r\n\t\t\t\t\t.getInputStream()));\r\n\t\t\tPrintStream sout = new PrintStream(s.getOutputStream());\r\n\t\t\tsout.println(msg);\r\n\t\t\tline = sin.readLine();\r\n\t\t\t// Check if connection is closed (i.e. for EOF)\r\n\t\t\tif (line == null) {\r\n\t\t\t\tlog.info(\"Connection closed by server.\");\r\n\t\t\t}\r\n\t\t} catch (IOException e) {\r\n\t\t\tif (s != null)\r\n\t\t\t\ts.close();\r\n\t\t\tthrow e;\r\n\t\t}\r\n\t\t// Always be sure to close the socket\r\n\t\tfinally {\r\n\t\t\ttry {\r\n\t\t\t\tif (s != null)\r\n\t\t\t\t\ts.close();\r\n\t\t\t} catch (IOException e2) {\r\n\t\t\t\tthrow e2;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\treturn line;\r\n\t}\r\n\r\n\r\n\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/SocketServer.java",
    "content": "package net.sf.borg.common;\r\n// This example is from the book _Java in a Nutshell_ by David Flanagan.\r\n// modified by Mike Berger - no license applies to this source file\r\nimport java.io.BufferedReader;\r\nimport java.io.IOException;\r\nimport java.io.InputStreamReader;\r\nimport java.io.PrintStream;\r\nimport java.net.ServerSocket;\r\nimport java.net.Socket;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * SocketServer is a thread that listens on a socket and starts a thread for\r\n * each incoming connection. Each connection thread calls back to the SocketHandler\r\n * to process each incoming message\r\n */\r\n\r\npublic class SocketServer extends Thread {\r\n\t\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n    \r\n\t// This class is the thread that handles all communication with a client\r\n\tstatic private class Connection extends Thread {\r\n\t    protected Socket client;\r\n\t    protected BufferedReader in;\r\n\t    protected PrintStream out;\r\n\t    private final SocketHandler handler_1;\r\n\r\n\t    // Initialize the streams and start the thread\r\n\t    public Connection(Socket client_socket, SocketHandler handler) {\r\n\t        this.client = client_socket;\r\n\t        this.handler_1 = handler;\r\n\t        try { \r\n\t            this.in = new BufferedReader(new InputStreamReader(this.client.getInputStream()));\r\n\t            this.out = new PrintStream(this.client.getOutputStream());\r\n\t        }\r\n\t        catch (IOException e) {\r\n\t            try { this.client.close(); } catch (IOException e2) {  /* empty */}\r\n\t            log.severe(\"Exception while getting socket streams: \" + e);\r\n\t            return;\r\n\t        }\r\n\t        this.setName(\"Socket Connection\");\r\n\t        this.start();\r\n\t    }\r\n\t    \r\n\t    // Provide the service.\r\n\t    // Read a line, reverse it, send it back.  \r\n\t    @Override\r\n\t    public void run() {\r\n\t       \r\n\t        try {\r\n\t            for(;;) {\r\n\t                // read in a line\r\n\t                String line = this.in.readLine();\r\n\t                if (line == null) break;\r\n\t                String output = this.handler_1.processSocketMessage(line);\r\n\t                this.out.println(output);\r\n\t            }\r\n\t        }\r\n\t        catch (IOException e) {  /* empty */ }\r\n\t        finally { try {this.client.close();} catch (IOException e2) {  /* empty */ } }\r\n\t    }\r\n\t}\r\n\t\r\n    protected ServerSocket listen_socket;\r\n    \r\n    /** the socket handler that will be called for each incoming message */\r\n    private final SocketHandler handler_;\r\n    \r\n    private static void fail(Exception e, String msg) {\r\n        log.severe(msg + \": \" +  e);\r\n    }\r\n    \r\n    /**\r\n     * Create a ServerSocket to listen for connections on;  start the thread.\r\n     * \r\n     * @param port the port\r\n     * @param handler the handler to call back with messages\r\n     */\r\n    public SocketServer(int port, SocketHandler handler) {\r\n        this.handler_ = handler;\r\n        try { this.listen_socket = new ServerSocket(port); }\r\n        catch (IOException e) { fail(e, \"Exception creating server socket\"); }\r\n        log.info(\"Server: listening on port \" + port);\r\n        this.setName(\"Socket Server\");\r\n        this.start();\r\n    }\r\n    \r\n    // The body of the server thread.  Loop forever, listening for and\r\n    // accepting connections from clients.  For each connection, \r\n    // create a Connection object to handle communication through the\r\n    // new Socket.\r\n    /* (non-Javadoc)\r\n     * @see java.lang.Thread#run()\r\n     */\r\n    @Override\r\n    public void run() {\r\n        try {\r\n            while(true) {\r\n                Socket client_socket = this.listen_socket.accept();\r\n                new Connection(client_socket, this.handler_);\r\n            }\r\n        }\r\n        catch (IOException e) { \r\n            fail(e, \"Exception while listening for connections\");\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Interface for a class that can process messages from a socket server\r\n     */\r\n    public static interface SocketHandler {\r\n\r\n        /**\r\n         * Process a message.\r\n         *\r\n         * @param msg the msg\r\n         *\r\n         * @return the response string to be sent back to the socket client\r\n         */\r\n        String processSocketMessage(String msg);\r\n\r\n    }\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/Warning.java",
    "content": "/*\r\nThis file is part of BORG.\r\n\r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n\r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n\r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\nCopyright 2004 by Mike Berger\r\n*/\r\n/*\r\n * Warning.java\r\n *\r\n * Created on April 14, 2004, 9:50 PM\r\n */\r\n\r\npackage net.sf.borg.common;\r\n\r\n/**\r\n * Warning is something to throw when there is a non fatal condition and the best way to handle it\r\n * is to use the throw mechanism. Catchers of this class should treat the message\r\n * as warning or informational text and should not show the stack trace to the user\r\n * \r\n */\r\npublic class Warning extends Exception {\r\n    \r\n\tprivate static final long serialVersionUID = -1336329298335625745L;\r\n\r\n\t/**\r\n\t * Creates a new instance of Warning.\r\n\t * \r\n\t * @param msg the msg\r\n\t */\r\n    public Warning(String msg) {\r\n        super(msg);\r\n    }\r\n    \r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/common/src/main/java/net/sf/borg/common/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Common utility classes\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NEW APPT *****\n\n-db_argument_is_missing = -db argument is missing\n\nAbout = About|A\n\nAbout_BORG = About BORG\n\nAction = Action|C\n\nAdd = Add|A|INSERT\n\nAddCat = Enter New Category:\n\nAdd_Log = Add Log Entry\n\nAdd_New = Add New|N|INSERT\n\nAdd_State = Add State\n\nAdd_Type = Add Type\n\nAddress = Address\n\nAddress_Book = Address Book|A|ctrl A\n\nAddress_Book_Entry = Address Book Entry\n\nAfter = After\n\nAll = All|A\n\nAll_Open = All Open\n\nApply_DB_Change = Apply DB Change|C\n\nAppointment_Editor = Appointment Editor\n\nAppointment_Editor_for_ = Appointment Editor for\n\nBefore = Before\n\nBerger-Organizer_v = Berger-Organizer \n\nBirthday = Birthday:\n\nBoxing_Day = Boxing Day\n\nBrowse = Browse|B\n\nBuild_Time\\:_ = \\\\nBuild Time: \n\nCLOSED = CLOSED\n\nCaldav-Overwrite-Warn = Really Overwrite the server? This cannot be undone!!\n\nCanada_Day = Canada Day\n\nCancel = Cancel\n\nCannot_Copy = There are unsaved changes. Cannot Copy.\n\nCategories = Categories|R\n\nCategory = Category|R\n\nCategory_Name_Required = Category Name Required\n\nCell_Phone\\: = Cell Phone:\n\nChange = Change|G|ctrl E\n\nCheckList = CheckList\n\nCheckList_Name = CheckList Name\n\nCheckLists = CheckLists\n\nChristmas = Christmas\n\nCivic_Holiday = Civic Holiday\n\nClear = Clear\n\nClear_DueDate = Clear Due Date\n\nClone = Clone|O|ctrl O\n\nClose = Close|L|ctrl X\n\nClosed = Closed|L\n\nColor = Color|O\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = Company:\n\nConfirm_DB_Change = Confirm DB Change\n\nConfirm_Delete = Confirm Delete\n\nCopy = Copy\n\nCopy_CheckList = Copy CheckList\n\nCould_not_parse_times\\:_ = Could not parse times:\n\nDataBase_Directory = DataBase Directory|D\n\nDatabaseInformation = Database Information\n\nDate = Date|D\n\nDay_View = Day View\n\nDaylight_Savings_Time = Daylight Savings Time\n\nDays = Days\n\nDaysLeftColors = Days-Left Colors\n\nDays_Left = Days Left\n\nDelete = Delete|D|DELETE\n\nDelete_Addresses = Delete Address(es)?\n\nDelete_CheckList = Delete CheckList\n\nDelete_Memo = Delete Memo\n\nDelete_One_Only = Delete One Only|Y\n\nDelete_State = Delete State\n\nDelete_Type = Delete Type\n\nDescription = Description\n\nDirectory_[ = Directory [\n\nDiscard_Text? = Discard Text?\n\nDismiss = Dismiss||alt X\n\nDone_(Delete) = Done (Delete)|E\n\nDone_(No_Delete) = Done (No Delete)|O\n\nDue = Due\n\nDueDate = Due Date\n\nDue_Date = Due Date|T\n\nEdit = Edit|E\n\nEditNew = Edit New\n\nEdited_CheckList = Current CheckList has been changed. Discard Changes?\n\nEdited_Memo = Current Memo has been changed. Discard Changes?\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailFrom = From Email Address\n\nEmailParameters = Email Parameters\n\nEnable_Daily_Email = Send Daily Email\n\nEnable_Email = Enable Email\n\nEncryptOnSave = Encrypt On Save\n\nEncryptedItem = <<<This  item  is Encrypted. Press Decrypt Button to Decrypt it >>>\n\nEncryptedItemShort = <<Encrypted>>\n\nEncryption = Encryption\n\nEndDate = EndDate\n\nEndTime = End Time\n\nEnterPasswordToDecrypt = Enter Keystore Password:\n\nEnter_CheckList_Name = Please enter the name for the new checklist\n\nEnter_Date = Enter Date\n\nEnter_Log = Enter Log Entry\n\nEnter_Memo_Name = Please enter the name for the new memo\n\nExisting_CheckList = A checklist already exists with that name\n\nExisting_Memo = A memo already exists with that name\n\nExit = Exit|X|alt X\n\nExport_Memo_Decrypt = Memo must be decrypted before exporting\n\nFather's_Day = Father's Day\n\nFax\\: = Fax:\n\nFilter\\: = Filter:|T\n\nFind = Find|F\n\nFirst = First\n\nFirst_Name\\: = First Name:\n\nFrequency = Frequency|Q\n\nFuture_Todo_Warn = This todo has future undone occurrences. Are you sure you want to delete?\n\nGo_To = Go To|G\n\nGround_Hog_Day = Ground Hog Day\n\nHalf_Day = Half Day|L\n\nHalloween = Halloween\n\nHelp = Help|H|F1\n\nHide = Hide\n\nHide_Pops = Hide All Reminders\n\nHoliday = Holiday|I\n\nHomeAddress = Home Address\n\nHome_City\\: = City:\n\nHome_Country\\: = Country:\n\nHome_Phone = Home Phone\n\nHome_Phone\\: = Home Phone:\n\nHome_State\\: = State:\n\nHome_Street_Address = Street Address:\n\nHome_Zip_Code\\: = Zip Code:|Z\n\nHour = Hour\n\nHours = Hours\n\nISO_week_number = Use ISO 8601 Week Numbering\n\nImport = Import\n\nImportFile = Import from File\n\nImportCarddav = Import from CardDav\n\nImportUrl = Import From URL\n\nImport_WARNING = Import WARNING\n\nImport_XML_error = Error importing appointment(s). Please check the XML file for invalid values.\n\nImport_error = Import failed with errors. Please see log for details.\n\nImporting_ = Importing\n\nIndependence_Day = Independence Day\n\nInitializing = Initializing\n\nInsert_Above = Insert Above\n\nInsert_Below = Insert Below\n\nInvalidMinute = Invalid value for minute\n\nItem = Item\n\nItem_ = Item\n\nItem_\\# = Item #|I\n\nKeyStore = KeyStore\n\nKey_Store_Not_Set = Key Store location is not set. To use encryption, it must be set in the options.\n\nLabor_Day = Labor Day\n\nLabour_Day_(Can) = Labour Day (Can)\n\nLast = Last\n\nLast_Name\\: = Last Name:\n\nLicense = License|L\n\nLogs = Logs\n\nLook_and_Feel\\: = Look and Feel:|L\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemo_Name = Memo Name\n\nMemo_not_exist = Cannot find memo in database. Has it been deleted from another window?\n\nMemorial_Day = Memorial Day\n\nMemos = Memos\n\nMinute = Minute\n\nMinutes = Minutes\n\nMonth_View = Month View\n\nMother's_Day = Mother's Day\n\nMove_To_Following_Day = Move to Following Day|M\n\nNEW_Item = NEW Item\n\nNew_CheckList = New CheckList\n\nNew_Memo = New Memo\n\nNew_State = New State\n\nNew_Task_Type = New Task Type\n\nNew_Theme_Name = Enter a new theme name\n\nNew_Year's_Day = New Year's Day\n\nNext_States = Next States\n\nNickname\\: = Nickname:\n\nNoOpenState = Error: No initial state found for task type\n\nNo_Categories = There are no categories\n\nNo_Key_Store = Could not read Key Store: \n\nNo_Print = Printing has not been implemented in this context\n\nNo_Specific_Time = No Specific Time|P\n\nNo_more = No more reminders for this appointment\n\nNot_Found_End = Not Found - End of Search...\n\nNotes = Notes\n\nNotice = Notice\n\nNow = -- Now! --\n\nOK = OK\n\nOPEN = OPEN\n\nOpen = Open|N\n\nOpen_Calendar = Open Calendar|C\n\nOptions = Options|O|ctrl O\n\nOverDue = Past Due:\n\nPA = PA|A\n\nPager\\: = Pager:\n\nPassword = Password|P\n\nPassword_Prompt = Please enter your BORG keystore password. Reason:\n\nPasswordWhitespace = Password must not be empty or all whitespace. KeyStore not created.\n\nPasswordsDoNotMatch = Passwords Do Not Match - KeyStore not created\n\nPaste = Paste\n\nPlease_choose_File_to_Import_From = Please choose File to Import From\n\nPlease_choose_directory_to_place_XML_files = Please choose directory for XML export\n\nPlease_enter_some_appointment_text = Please enter some appointment text\n\nPlease_select_a_state = Please select a state\n\nPlease_select_a_type = Please select a type\n\nPlugins = Plugins\n\nPopup_Times = Popup Times\n\nPresidents_Day = Presidents Day\n\nPri = Pri|P\n\nPrint = Print|P|ctrl P\n\nPrint_Chooser = Print Chooser\n\nPrint_In_Color? = Print In Color?|C\n\nPriority = Priority\n\nPrivate = Private|V\n\nProjectInformation = Project Information\n\nProperties = Properties\n\nQuick_Note = Quick Note\n\nQuiet = Quiet\n\nReally_Delete_ = Really Delete\n\nReally_Delete_Apps = Really Delete Appointment(s)\n\nReally_change_the_database? = Really change the database?\n\nReally_delete_number_ = Really delete number\n\nRecurrence = Recurrence\n\nRemembrance_Day = Remembrance Day\n\nReminder = Reminder\n\nReminder_Notice = BORG Reminder Notice\n\nRemove = Remove\n\nRename = Rename\n\nRename_State = Rename State\n\nRename_Type = Rename Type\n\nReset_Task_States_to_Default = Reset Task States to Default|R\n\nResolution = Resolution\n\nRestart_Warning = You must restart the program for these changes to apply. The program will now exit...\n\nRunSQL = Run SQL\n\nSMTP_Port = SMTP Port\n\nSMTP_Server = SMTP Server|S\n\nSMTP_password = SMTP Password (Optional)\n\nSMTP_user = SMTP User (Optional)\n\nSave = Save|S|ctrl S\n\nSave_&_Close = Save & Close|C|\n\nSave_CheckList = Save CheckList\n\nSave_Memo = Save Memo\n\nScreen_Name\\: = Screen Name:\n\nSearchString = Search String\n\nSearch_For = Search For...\n\nSearch_Next = Search Next...\n\nSearch_Results = Search Results\n\nSelect = Select\n\nSelectKeyStore = Select Key Store File\n\nSelect_CheckList_Warning = A checklist must be selected to perform this action\n\nSelect_Memo_Warning = A memo must be selected to perform this action\n\nSelect_initial_state = Select Initial State\n\nSelect_next_state = Select next state\n\nSet_DueDate = Set Due Date\n\nSet_Initial_State = Set Initial State\n\nShow_Canadian_Holidays = Show Canadian Holidays|C\n\nShow_Pops = Show All Reminders\n\nShow_Private_Appointments = Private Appointments\n\nShow_Public_Appointments = Show Public Appointments|P\n\nShow_Stack_Trace = Show Stack Trace\n\nShow_U.S._Holidays = Show U.S. Holidays|U\n\nSt._Patrick's_Day = St. Patrick's Day\n\nStandard_Time = Standard Time\n\nStartDate = Start Date\n\nStartToSysTray = Start to System Tray\n\nStart_After_End = Start Date cannot be later than the End Date\n\nStart_Date = Start Date|D\n\nStart_Time\\: = Start Time:|A\n\nState_Change = State Change\n\nStates = States\n\nStatus = Status|U\n\nSubTasks = SubTasks\n\nSync = Sync\n\nSync_Cleanup = Sync (with Cleanup)\n\nSync-Not-Set = Sync is not on. Please fill in the Sync settings in the Options\n\nSync-Warn = There are items that have not been synced. Continue with shutdown?\n\nTaskInformation = Task Information\n\nTask_Created = Task Created\n\nTask_State_Editor = Task State Editor\n\nTask_Types = Task Types\n\nText = Text\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTheme = Theme\n\nTime = Time\n\nTimes = Times|M\n\nTo_Do = To Do|T|ctrl T\n\nTo_Do_List = To Do List|D\n\nToday = Today|T|HOME\n\nType = Type|Y\n\nURL_is_Empty = URL is Empty\n\nUncheck_All = Uncheck All\n\nUnhide_All = Unhide All\n\nUntil = Until\n\nUse_24_hour_time_format = Use 24 hour time format|2\n\nUse_system_beep = Use System Beep\n\nUserColorScheme = User Color Scheme\n\nVacation = Vacation|C\n\nValentine's_Day = Valentine's Day\n\nVeteran's_Day = Veteran's Day\n\nVictoria_Day = Victoria Day\n\nView = View\n\nWeb_Page\\: = Web Page:\n\nWeek_Starts_with_Monday = Week Starts with Monday|M\n\nWeek_View = Week View\n\nWeek_View_End_Hour\\:_ = Day End Hour:|E\n\nWeek_View_Start_Hour\\:_ = Day Start Hour:|S\n\nWholeWord = Whole Word\n\nWorkAddress = Work Address\n\nWork_City\\: = City:\n\nWork_Country\\: = Country:\n\nWork_Phone = Work Phone\n\nWork_Phone\\: = Work Phone:\n\nWork_State\\: = State:\n\nWork_Street_Address = Street Address:\n\nWork_Zip_Code\\: = Zip Code:\n\nYear_View = Year View\n\nYour_Email_Address = To Email Address\n\n]_does_not_exist = ] does not exist\n\n]_is_not_a_directory = ] is not a directory\n\n__through__ = through\n\naddcat = Add New Category|A\n\naddresses = Addresses\n\nall_undos = Show Entire Undo Stack (read-only)\n\nallow_self_signed = Allow Self-Signed SSL\n\nappearance = Appearance\n\napply = Apply|A\n\nappointment = Appointment\n\nappointments = Appointments\n\nappt_error = The appointment window will close due to a prior error. Perhaps an appointment was removed from the ToDo window while the editor was open. If not, restart Borg\n\napptlist = Appointment List\n\nappttext = Appointment Text\n\nappttime = Appointment Time\n\natt_folder_err = Cannot create attachment folder:\n\natt_not_found = Could not find linked object\n\natt_owner_null = Cannot save link before this new item is saved to the database\n\nattach_file = Attach File\n\naudit_logging = Audit Logging\n\nbackup_dir = Backup Folder\n\nbackup_dir_error = Cannot write to backup folder {0}. Please check that it exists and is writable. Backup will not be done.\n\nbackup_notice = Check here to also write backup data to\n\nbad_db_2 = \\\\nIf a bad database dir is causing the error, please click OK to change it.\n\nbd_rem_days = Birthday Reminder Days (-1 to disable)\n\nbeep_options = Reminder Sound\n\nbiweekly = biweekly\n\nblack = black\n\nblue = blue\n\nborg_backup = BORG Backup File\n\ncalShowSubtask = Show Subtasks on Calendar/Todo\n\ncalShowTask = Show Tasks on Calendar/Todo\n\ncase_sensitive = Case Sensitive\n\ncat_choose = Choose Category\n\ncatchooser = Category Chooser\n\nchangedate = Change Date|H\n\nchg_cat = Change Category\n\nchg_color = Change Color\n\nchoose = choose...\n\nchoose_file = Choose File\n\nchoosecat = Choose Categories to Display|C\n\nclear_all = Clear All|C\n\nclear_undos = Clear Undo Log\n\nclose_date = Close Date\n\nclose_proj_warn = Cannot close project. At least one child is not closed.\n\nclose_tabs = Close All Tabs\n\ncollapse = Collapse\n\nconfirm_overwrite = Confirm Overwrite\n\ncontact = Contact Information\n\ncontrib = Mohan Embar, Stepan Borodulin, Josie Stauffer, Meianandh, Travis Hein\n\ncontributions_by = Thanks for Contributions to:\n\ncopy_appt = Copy Appointment\n\ncopyright = Copyright\n\ncreate_key_store = Key Store File does not exist. Please enter a password to create a new Key Store, or cancel to abort. You must enter the password twice for confirmation.\n\ncreated = Created\n\ncustom_times_header = Popup Reminder Times for\n\ndaily = daily\n\ndb_set_to = \\\\n\\\\nYour database dir is set to:\n\ndb_time_error = The database shutdown timestamp is older than the shutdown timestamp in the BORG preferences. It is possible that BORG failed to properly write to the database when last shutdown. Please check for lost data, unless you have been using multiple databases.\n\ndecrypt = Decrypt\n\ndefault = default\n\ndefaultfg = default fg\n\ndel_task_warning = Tasks exist for type {0}. Cannot delete task type {0}.\n\ndel_tip = Delete all occurrences (repeats) of an appointment\n\ndelcat_warn = WARNING! Deleting ALL appointments and tasks for category\n\ndelete_cat = Delete Category|D\n\ndelete_cat_choose = Choose Category to Delete\n\ndelete_selected = Delete Selected\n\ndeleted = Deleted\n\ndlist = Select Days\n\ndock = Dock\n\ndone = done\n\ndoo_tip = Delete only the current occurrence of a repeating appointment\n\ndraw_background_pills = Draw Background Pills\n\nduration = Duration\n\ndview_font = Day View\n\nedit_types = Edit Task Types and States|E\n\nelapsed_time = Elapsed\n\nemailNotEnabled = Email is not enabled in the options.\n\nempty_desc = Please enter a Description\n\nempty_summ = Please enter a Summary\n\nenable_popups = Enable Popup Reminders\\n|P\n\nenable_systray = Enable System Tray Icon (requires restart)\n\nenable_tls = Enable TLS\n\nenturl = Enter URL:\n\nep = Edit Preferences|E\n\nexit_no_backup = Exit without creating backup file\n\nexpXML = Export Database\n\nexpand = Expand\n\nexport = Export\n\nexportToIcalZipFile = Export to yearly ICal files\n\nexportToFile = Export To File\n\nexport_prefs = Export Preferences\n\nfont_chooser = Font Chooser\n\nfonts = Fonts\n\nforever = Repeat Forever|F\n\nfuture_birthday = Warning: saving a birthday that is set to a future date\n\nfuture_todo_rpts = Future Todo Repeats\n\ngradient_appts = Draw Gradient Color in Appointment Boxes\n\ngreen = green\n\nh2info = H2 Information\n\nhaslinks = Has Links\n\nhide_strike = Strike-through Items\n\nhistory = History\n\nhsqldb = HSQLDB\n\nhsqldbinfo = HSQLDB Information\n\nical_export_todos = Export Todos as VTODO objects\n\nical_files = ICAL Files\n\nical_options = Ical Options\n\niconSize = Icon Size (Restart Required)\n\nimpXML = Import single database table\n\nimpexpMenu = Import / Export|I\n\nimport_format_error = Import failed. File format is not valid.\n\nimport_prefs = Import Preferences\n\nimport_zip = Import Entire Backup Zipfile\n\njdbc = Generic JDBC\n\nlink_file = Link file\n\nlinks = Links\n\nlocale = Locale (Restart Required)|O\n\nmax_length = The maximum length of {0} is {1}\n\nmemo = Memo\n\nmin_aft_app = minutes after the appointment\n\nmin_bef_app = minutes before the appointment\n\nminute_reminder = minute reminder\n\nminutes_ago = minutes ago!\n\nmisc = Miscellaneous\n\nmonthly = monthly (date)\n\nmonthly_day = monthly (day)\n\nmonthly_day_last = monthly (day) last\n\nmview_font = Print Font\n\nmwf = Mon-Wed-Fri\n\nnavy = navy\n\nndays = Every N Days\n\nnewDate\\: = New Date:|N\n\nnmonths = Every N Months\n\nnoOutput = SQL Command Returned no output or errors\n\nno_appts_selected = One or more appointments must be selected\n\nno_copy_enc = Cannot copy an Encrypted Appointment using the Editor.\n\nno_search_results = The search returned no results\n\nno_sound = no sound\n\nno_subtask_action = Cannot perform the selected action on a subtask from the task list UI. Use the task UI.\n\nno_undos = Nothing to Undo\n\nnummonths = Number of Months to Print?\n\nnweeks = Every N Weeks\n\nnyears = Every N Years\n\non_shutdown = On Shutdown:\n\nonce = once\n\nopen_subtasks = Cannot Close Task, Subtasks are not all closed\n\nopen_tasks = Open Tasks\n\noverwrite_warning = File Exists. OK to Overwrite File:\n\nparent = Parent\n\nplease_confirm = Please Confirm\n\npopup_reminders = Popup Reminders\n\nproj_sd_warning = Cannot save task. The task's start date is earlier than the project's start date\n\nprojchild_warning = Cannot save project. A child project has a due date later than the project's due date\n\nprojdd_warning = Cannot save project. A child task has a due date later than the project's due date\n\nproject = Project\n\nproject_cycle = Cannot save project. A project cannot be its own ancestor.\n\nproject_not_found = Error: Project not found in database. Has it been deleted?\n\nproject_tree = Project Tree\n\nprojects = Projects\n\nprojpar_warning = Cannot save project. Its due date is later than its parent project's due date\n\nprompt_for_backup = Prompt for backup action\n\npw_time = Password Expiration (secs):\n\nrecur_compat = Error: Recurrence is not compatible with the appointment date\n\nred = red\n\nreload = Reload\n\nremcat = Remove Unused Categories|R\n\nreminder_bg = Reminder Background\n\nreminder_list = Show All Reminders in a Single Window\n\nreminder_time = Reminder Email Time|R\n\nrename_task_warning = Tasks exist for type {0}. If you proceed, the tasks states will be saved to the database and the existing tasks will be updated to the new type. Proceed?\n\nrepeating = Repeating\n\nresendEmail = Send Email Reminder\n\nreset_state_warning = This will reset any customizations you have made to the task model\\\\nThe task model will be reset to the shipped default.\\\\nDo you really want to do this?\\\\n\n\nrestore_defaults = restore defaults\n\nrlsnotes = Release Notes\n\nsave_Def = Save Defaults\n\nsd_dd_warn = Error: Due Date cannot be before the Start Date\n\nsd_tip = Save current values as new Appointment defaults\n\nsearch_color_warn = Warning: only Appointment colors can be changed from the Search Window\n\nselect_all = Select All|S\n\nselect_appt = Please select one appointment\n\nselect_export_dir = Select Export Directory\n\nselect_link_type = Select Object Type for Link\n\nselectdb = Database is not set. Please enter database settings.\n\nsend_reminder = Send Reminder Email|E\n\nset_appt_font = Month View\n\nset_def_font = Default\n\nshow_closed = Show Closed Projects\n\nshow_closed_tasks = Show Closed Tasks\n\nshow_date_in_systray = Show Date in System Tray\n\nshow_rpt_num = Show Repeat Number|W\n\nshow_subtasks = Show Subtasks\n\nshow_task_reminders = Show Reminders for Tasks, Projects, and Subtasks\n\nshow_task_status_in_tree = Show Task Number and Status in Task Tree\n\nshow_taskbar_reminders = Show Taskbar Notifications\n\nshowdoy = Show Day Of Year|Y\n\nshutdown = Shutting Down....Please wait\n\nshutdown_options = Select OK to shutdown\n\nsocket_port = Socket Port (-1=disabled, requires restart)\n\nsocket_warn = Socket Port must be an integer\n\nsort_by_priority = Sort Appointments By Priority\n\nsplash = Show splash screen|S\n\nsqliteinfo = SQLITE Information\n\nsrch = Search|S|F3\n\nstackonerr = Show Stack Trace Option on Error Dialogs|K\n\nstartupViews = Startup Views\n\nstdd_warning = Cannot save task. A subtask has a due date later than the task's due date\n\nstrike = strike-through\n\nstripecolor = Table Stripe Color\n\nstsd_warning = Cannot save task. A subtask has a start date earlier than the task's start date\n\nsubject = Subject\n\nsubscribed_cals = Subscribed Calendars\n\nsubtask = Subtask\n\nsubtask_id = Subtask ID\n\nsummary = Summary\n\nsyncing = Syncing....Please wait\n\ntask = Task\n\ntaskOptions = Task Options\n\ntask_abbrev = Show Task Numbers on Calendar\n\ntask_parent_closed = Cannot save task in open state when parent project is closed.\n\ntaskdd_warning = Cannot save task with a due date later than the project due date\n\ntasks = Tasks\n\ntodoOptions = Todo Options\n\ntodo_only_show_current = For repeating ToDo's, only show the current instance\n\ntodo_option_auto_clear_text = Clear To Do text field on Add\n\ntodo_option_auto_date_today = Default date to today\n\ntodo_reminder_freq = Minutes between reminders for untimed todos\n\ntodomissingdata = Please enter both the todo text and date.\n\ntodoquickentry = Quick Todo\n\ntotal_tasks = Total Tasks\n\ntranslations = Spanish: Josu Gil Arriortua, Pedro Falcato (AR), Redy Rodr\\u00EDguez   Russian: Stepan Borodulin\\\\nGerman: Joerg Gabel, Philip Oettershagen, and Stefan Pottinger\\\\nItalian: Piero Trono   Polish: Rafa\\u0142 Gawro\\u0144ski, Jerzy Luszawski\\\\nSimplified Chinese: LaughCry\\\\nDutch: Stefan Bruckner, Rudy Defrene (BE)\\\\nPortugu\\u00EAs (Brasil): Abinoam Marques Jr.\n\ntray_bg = Tray Icon Background\n\ntray_fg = Tray Icon Foreground\n\ntray_font = Tray Icon Font\n\ntruncate_appts = Truncate Appointments in Month View|T\n\ntth = Tues-Thurs\n\nucolortext1 = use user colors in todo list|T\n\nucolortext10 = holidays\n\nucolortext11 = birthdays\n\nucolortext12 = default bg\n\nucolortext13 = holiday\n\nucolortext14 = halfday\n\nucolortext15 = vacation\n\nucolortext16 = today\n\nucolortext17 = weekend\n\nucolortext18 = weekday\n\nucolortext2 = mark todo on calendar\n\nucolortext4 = text color 1\n\nucolortext5 = text color 2\n\nucolortext6 = text color 3\n\nucolortext7 = text color 4\n\nucolortext8 = text color 5\n\nucolortext9 = tasks\n\nuncategorized = <No Category>\n\nundo = Undo\n\nunknown = unknown\n\nunsync-Warn = **CAUTION** This will remove all google sync data from the database. This can be used to prepare for full syncing to an empty google calendar. It will break the relationship with all current google events. Really Proceed?\n\nuntil_date_error = Repeat Until date must be later than the appointment date\n\nuntil_null_error = Please enter the repeat until date\n\nupdated = Updated\n\nurl = URL\n\nuse_ssl = Use SSL\n\nuser = user\n\nvacation_warning = Cannot select both Full and Half Day Vacation for the same Appointment.\n\nverbose_logging = Verbose Logging\n\nview_log = View Log\n\nviewchglog = Change Log|V\n\nweekdays = Monday-Friday\n\nweekends = Saturday and Sunday\n\nweekly = weekly\n\nwhite = white\n\nwrite_backup_file = Backup to a file\n\nwview_font = Week View\n\nxml_file = XML Files\n\nyearly = yearly\n\nyears_to_export = Years To Export\n\nyview_font = Year View\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_de.properties",
    "content": "\n*****_NEW_APPT_***** = Neuer Termin\n\n-db_argument_is_missing = Datenbank-Wert fehlt\n\nAbout = \\u00DCber...|A\n\nAbout_BORG = \\u00DCber BORG\n\nAction = Aktion|C\n\nAdd = Hinzuf\\u00FCgen|A|INSERT\n\nAddCat = Neue Kategorie einf\\u00FCgen:\n\nAdd_Log = Neuer Protokolleintrag\n\nAdd_New = Neuer Eintrag|N|INSERT\n\nAdd_State = Neuer Status\n\nAdd_Type = Neuer Typ\n\nAddress = Adresse\n\nAddress_Book = Adressbuch|A|ctrl A\n\nAddress_Book_Entry = Adressbucheintrag\n\nAfter = nachher\n\nAll = Alle|A\n\nAll_Open = Alle offenen\n\nApply_DB_Change = DB-\\u00C4nderung anwenden|C\n\nAppointment_Editor = Termin-Editor\n\nAppointment_Editor_for_ = Termin f\\u00FCr den \n\nBefore = bevor\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Geburtstag:|B\n\nBoxing_Day = Boxtag\n\nBrowse = Durchsuchen|B\n\nBuild_Time\\:_ = \\\\nBuild Zeit:\\\\n\n\nCLOSED = GESCHLOSSEN\n\nCanada_Day = Kanadatag\n\nCancel = Abbrechen\n\nCategories = Kategorien|R\n\nCategory = Kategorie|R\n\nCell_Phone\\: = Handy:\n\nChange = \\u00C4ndern|N|ctrl E\n\nChristmas = Weihnachten\n\nCivic_Holiday = St\\u00E4dtischer Feiertag\n\nClear_DueDate = L\\u00F6sche Enddatum\n\nClone = Duplizieren|O|ctrl O\n\nClose = Schlie\\u00DFen|L|ctrl X\n\nClosed = Schlie\\u00DFen|L\n\nColor = Farbe|O\n\nColumbus_Day = Kolumbustag \n\nCommonwealth_Day = Commonwealth-Tag\n\nCompany = Firma:|F\n\nConfirm_DB_Change = Datenbank-\\u00C4nderung best\\u00E4tigen\n\nConfirm_Delete = Best\\u00E4tige l\\u00F6schen\n\nCopy = Kopieren\n\nCould_not_parse_times\\:_ = Konnte Zeiten nicht verarbeiten: \n\nDataBase_Directory = Datenbankverzeichnis|D\n\nDatabaseInformation = Datenbank\n\nDate = Datum|D\n\nDay_View = Tagesansicht\n\nDaylight_Savings_Time = Sommerzeit\n\nDays = Tage\n\nDays_Left = Tage \\u00FCbrig\n\nDelete = L\\u00F6schen|D|DELETE\n\nDelete_Addresses = Adresse(n) l\\u00F6schen?\n\nDelete_Memo = Notiz l\\u00F6schen\n\nDelete_One_Only = Einzeln l\\u00F6schen|Y\n\nDelete_State = L\\u00F6schen\n\nDelete_Type = L\\u00F6schen\n\nDescription = Beschreibung\n\nDirectory_[ = Verzeichnis[\n\nDiscard_Text? = Text verwerfen?\n\nDismiss = Schlie\\u00DFen||alt X\n\nDone_(Delete) = Fertig(l\\u00F6schen)|E\n\nDone_(No_Delete) = Fertig (nicht l\\u00F6schen)|O\n\nDueDate = Enddatum\n\nDue_Date = Enddatum|T\n\nEdit = Bearbeiten|E\n\nEditNew = Neuen Termin bearbeiten\n\nEmail = Email\n\nEmail\\: = Email:|E\n\nEmailParameters = Email\n\nEnable_Email = Email verwenden|E\n\nEndDate = Enddatum\n\nEnter_Log = Protokolleintrag eintragen\n\nEnter_Memo_Name = Bitte Name f\\u00FCr die neue Notiz eingeben\n\nExisting_Memo = Es existiert bereits eine Notiz mit diesem Namen\n\nExit = Beenden|X|alt X\n\nFather's_Day = Vatertag\n\nFax\\: = Fax:|A\n\nFilter\\: = Filter:|T\n\nFirst = Vorname|V|\n\nFirst_Name\\: = Vorname:|V\n\nFrequency = H\\u00E4ufigkeit|Q\n\nGo_To = Gehe zu|G\n\nGround_Hog_Day = Murmeltiertag \n\nHalf_Day = Halber Tag|L\n\nHalloween = Halloween\n\nHelp = Hilfe|H|F1\n\nHoliday = Feiertag|I\n\nHomeAddress = Adresse (Privat)\n\nHome_City\\: = Ort:|C\n\nHome_Country\\: = Land:|U\n\nHome_Phone = Telefon (Privat)\n\nHome_Phone\\: = Telefon (Privat):|H\n\nHome_State\\: = Bundesland:|T\n\nHome_Street_Address = Adresse, Hausnummer:|A\n\nHome_Zip_Code\\: = PLZ:|Z\n\nHour = Stunde\n\nHours = Stunden\n\nISO_week_number = Benutze ISO 8601 f\\u00FCr Wochennummerierung\n\nImport = Importiere\n\nImport_WARNING = ACHTUNG Import\n\nImporting_ = Importiere \n\nIndependence_Day = Unabh\\u00E4ngigkeitstag\n\nInitializing = Initialisiere\n\nItem = Eintrag\n\nItem_ = Eintrag\n\nItem_\\# = Eintrag #|I\n\nLabor_Day = Tag der Arbeit \n\nLabour_Day_(Can) = Tag der Arbeit (Can)\n\nLast = Nachname\n\nLast_Name\\: = Nachname:|N\n\nLicense = Lizenz|L\n\nLook_and_Feel\\: = Aussehen:|L\n\nMartin_Luther_King_Day = Martin Luther King Tag \n\nMemo_Name = Notizname\n\nMemorial_Day = Gedenktag \n\nMemos = Notizen\n\nMinute = Minute\n\nMinutes = Minuten\n\nMonth_View = Monatsansicht\n\nMother's_Day = Muttertag \n\nMove_To_Following_Day = Zum n\\u00E4chsten Tag springen|M\n\nNEW_Item = Neuer Eintrag\n\nNew_Memo = Neue Notiz\n\nNew_State = Neuer Status\n\nNew_Task_Type = Neuer Aufgabentyp\n\nNew_Year's_Day = Neujahr\n\nNext_States = Nachfolger-Stati\n\nNickname\\: = Spitzname:\n\nNoOpenState = Fehler: Kein Anfangszustand gefunden f\\u00FCr Aufgabentyp\n\nNo_Specific_Time = Ganztags|G\n\nNo_more = Keine weiteren Erinnerungen f\\u00FCr diesen Termin\n\nNotes = Bemerkungen\n\nNotice = Mitteilung\n\nNow = -- Jetzt! --\n\nOK = OK\n\nOPEN = Offen\n\nOpen = \\u00D6ffnen|N\n\nOpen_Calendar = Kalender|C\n\nOptions = Optionen|O|ctrl O\n\nPA = Person|A\n\nPager\\: = Pager:|P\n\nPassword = Passwort|P\n\nPlease_choose_File_to_Import_From = Bitte w\\u00E4hlen Sie die zu importierende Datei aus\n\nPlease_choose_directory_to_place_XML_files = W\\u00E4hlen Sie ein Verzeichnis f\\u00FCr die XML-Dateien\n\nPlease_enter_some_appointment_text = Bitte Beschreibung eingeben\n\nPlease_select_a_state = Einen Status ausw\\u00E4hlen\n\nPlease_select_a_type = Einen Typ ausw\\u00E4hlen\n\nPopup_Times = Popup-Zeiten\n\nPresidents_Day = Pr\\u00E4sidententag \n\nPri = Priorit\\u00E4t|P\n\nPrint = Drucken|P|ctrl P\n\nPrint_Chooser = Drucker-W\\u00E4hler\n\nPrint_In_Color? = Druck in Farbe|C\n\nPrivate = Privat|V\n\nProjectInformation = Projekt-Information\n\nProperties = Eigenschaften\n\nReally_Delete_ = Wirklich l\\u00F6schen \n\nReally_change_the_database? = Datenbank wirklich \\u00E4ndern?\n\nReally_delete_number_ = Sind Sie sicher, das sie die Nummer l\\u00F6schen wollen ?\n\nRecurrence = Wiederholung\n\nRemembrance_Day = Volkstrauertag\n\nReminder = Erinnerung\n\nReminder_Notice = BORG Erinnerung\n\nRename_State = Umbenennen\n\nRename_Type = Umbenennen\n\nReset_Task_States_to_Default = Standard-Definitionen wiederherstellen\n\nResolution = Aufl\\u00F6sung\n\nRunSQL = SQL-Befehl\n\nSMTP_Server = SMTP Server|S\n\nSMTP_password = SMTP Passwort (Optional)\n\nSMTP_user = SMTP Benutzer (Optional)\n\nSave = Speichern|S|ctrl S\n\nSave_Memo = Notiz speichern\n\nScreen_Name\\: = Anzeigename:|M\n\nSearchString = Suchbegriff\n\nSearch_Results = Suche Ergebnisse\n\nSelect = \\u00DCbernehmen\n\nSelect_Memo_Warning = Notiz muss ausgew\\u00E4hlt sein, um diesen Befehl auszuf\\u00FChren\n\nSelect_initial_state = Anfangszustand ausw\\u00E4hlen\n\nSelect_next_state = W\\u00E4hle n\\u00E4chsten Status aus\n\nSet_DueDate = Setze Enddatum\n\nSet_Initial_State = Anfangszustand setzen\n\nShow_Canadian_Holidays = Kanadische Feiertage zeigen|C\n\nShow_Private_Appointments = Private Termine zeigen|V\n\nShow_Public_Appointments = \\u00D6ffentliche Termine zeigen|P\n\nShow_Stack_Trace = Zeige Stack-Trace\n\nShow_U.S._Holidays = US-Feiertage zeigen|U\n\nSt._Patrick's_Day = St. Patrick's Tag\n\nStandard_Time = Standardzeit\n\nStartDate = Startdatum\n\nStart_Date = Startdatum|D\n\nStart_Time\\: = Startzeit:|A\n\nState_Change = Zustandswechsel\n\nStates = Status\n\nStatus = Status|U\n\nSubTasks = Unteraufgaben\n\nTaskInformation = Aufgaben\n\nTask_Created = Aufgabe erstellt\n\nTask_State_Editor = Termintyp-Editor\n\nTask_Types = Aufgabentyp\n\nText = Text\n\nThanksgiving = Erntedankfest\n\nThanksgiving_(Can) = Erntedankfest (Can)\n\nTime = Zeit\n\nTimes = Anzahl|M\n\nTo_Do = ToDo-Liste|T|ctrl T\n\nTo_Do_List = ToDo-Liste|D\n\nToday = Heute|T|HOME\n\nType = Typ|Y\n\nUse_24_hour_time_format = 24-Stunden-Ansicht|2\n\nUse_system_beep = Systemlautsprecher\n\nUserColorScheme = Farben\n\nVacation = Urlaub|C\n\nValentine's_Day = Valentinstag\n\nVeteran's_Day = Veteranentag\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = Homepage:|G\n\nWeek_Starts_with_Monday = Woche startet Montags|M\n\nWeek_View = Wochenansicht\n\nWeek_View_End_Hour\\:_ = Woche endet um:|E\n\nWeek_View_Start_Hour\\:_ = Woche startet um:|S \n\nWorkAddress = Adresse (Gesch\\u00E4ftlich)\n\nWork_City\\: = Ort:|I\n\nWork_Country\\: = Land:|R\n\nWork_Phone = Telefon (Gesch\\u00E4ftlich)\n\nWork_Phone\\: = Telefon (Gesch\\u00E4ftlich):|W\n\nWork_State\\: = Bundesland:|E\n\nWork_Street_Address = Adresse, Hausnummer:|D\n\nWork_Zip_Code\\: = PLZ:|O\n\nYour_Email_Address = Ihre E-Mail-Adresse|Y\n\n]_does_not_exist = ] Existiert nicht\n\n]_is_not_a_directory = ] Ist kein Verzeichnis\n\n__through__ = bis\n\naddcat = Neue Kategorie|A\n\naddresses = Adressen\n\nappearance = Erscheinungsbild\n\napply = Speichern|A\n\nappointments = Termine\n\nappt_error = Das Termin-Fenster wird durch einen fr\\u00FCheren Fehler geschlossen.\\\\nM\\u00F6glicherweise wurde ein Termin aus dem To Do-Fenster gel\\u00F6scht, w\\u00E4hrend der Editor offen war. Falls nicht, BORG bitte neu starten.\n\napptlist = Terminliste\n\nappttext = Termintext\n\nappttime = Zeit\n\nbad_db_2 = \\\\nWenn ein fehlerhaftes Datenbank-Verzeichnis den Fehler verursachte,\\\\n bitte 'JA' w\\u00E4hlen, um es zu \\u00E4ndern.\n\nbiweekly = zweiw\\u00F6chentlich\n\nblack = schwarz\n\nblue = blau\n\ncalShowSubtask = Unteraufgaben in Kalender/ToDo-Liste anzeigen\n\ncalShowTask = Aufgaben in Kalender/ToDo-Liste anzeigen\n\ncase_sensitive = Gro\\u00DF-/Kleinschreibung beachten\n\ncat_choose = W\\u00E4hle Kategorie\n\ncatchooser = Kategorie-W\\u00E4hler\n\nchangedate = \\u00C4ndere Datum|H\n\nchg_cat = Wechsele Kategorie\n\nchoose_file = W\\u00E4hle Datei\n\nchoosecat = Kategorie zum Anzeigen ausw\\u00E4hlen|C\n\nclear_all = Auswahl aufheben|C\n\nclose_date = Enddatum\n\nclose_proj_warn = Kann Projekt nicht beenden. Mindestens ein Untereintrag wurde noch nicht beendet.\n\nconfirm_overwrite = \\u00DCberschreiben best\\u00E4tigen\n\ncontact = Kontaktinformationen\n\ncontributions_by = Dank f\\u00FCr Beitr\\u00E4ge geht an:\n\ncopy_appt = Termin kopieren\n\ncopyright = Copyright\n\ncreated = Erstellt\n\ncustom_times_header = Popup-Erinnerungszeiten f\\u00FCr\n\ndaily = t\\u00E4glich\n\ndb_set_to = \\\\n\\\\nDas Datenbankverzeichnis ist gesetzt auf: \n\ndel_tip = L\\u00F6sche alle Vorkommen des sich wiederholenden Termins\n\ndelcat_warn = Achtung! L\\u00F6schen ALLER Termine und Aufgaben f\\u00FCr die Kategorie\n\ndelete_cat = L\\u00F6sche Kategorie|D\n\ndelete_cat_choose = W\\u00E4hle Kategorie zum l\\u00F6schen\n\ndelete_selected = L\\u00F6sche ausgew\\u00E4hlte\n\ndeleted = Gel\\u00F6scht\n\ndlist = Tage ausw\\u00E4hlen\n\ndone = Fertig\n\ndoo_tip = L\\u00F6sche nur dieses Vorkommen des sich wiederholenden Termins\n\ndview_font = Tagesansicht\n\nedit_types = Typen und Stati bearbeiten|E\n\nelapsed_time = Verstrichene Tage\n\nempty_desc = Bitte eine Beschreibung eintragen\n\nenable_popups = Aktiviere PopUp-Erinnerungen\\n|P\n\nenable_systray = Icon im System-Tray zeigen (Neustart erforderlich)\n\nenturl = URL eingeben:\n\nep = Einstellungen|E\n\nexpXML = XML: Export|E\n\nexport = Export\n\nfonts = Schriftarten\n\nforever = Unendlich wiederholen|F\n\ngreen = gr\\u00FCn\n\nh2info = H2 Information\n\nhistory = Verlauf\n\nhsqldb = HSQLDB\n\nhsqldbinfo = HSQLDB Information\n\nimpXML = XML: Import|I\n\nimpexpMenu = Import/Export|I\n\njdbc = Generic JDBC\n\nlocale = Land (Neustart erforderlich)|N\n\nmin_aft_app = Minuten nach dem Termin\n\nmin_bef_app = Minuten vor dem Termin\n\nminute_reminder = Minuten-Erinnerung\n\nminutes_ago = Minuten vergangen!\n\nmisc = Verschiedenes\n\nmonthly = monatlich (Datum)\n\nmonthly_day = monatlich (Tag)\n\nmview_font = Ausdruck\n\nmwf = Mo-Mi-Fr\n\nnavy = dunkelblau\n\nndays = Jeden N Tag\n\nnewDate\\: = Neues Datum:|N\n\nnoOutput = SQL-Befehl lieferte keine Ausgabe oder Fehler\n\nnummonths = Wieviele Monate drucken?\n\nonce = einmalig\n\nopen_subtasks = Kann Aufgabe nicht beenden, da mindestens eine Unteraufgaben noch nicht beendet ist.\n\nopen_tasks = Offene Aufgaben\n\noverwrite_warning = Datei exitiert beretis. OK zum \\u00FCberschreiben: \n\npopup_reminders = PopUp-Erinnerungen\n\nprojdd_warning = Kann Projekt nicht speichern. Ein Untereintrag hat ein Enddatum nach dem des Projekts\n\nproject = Projekt\n\nprojects = Projekte\n\nrecur_compat = Fehler: Wiederholung ist nicht kompatibel zum Termindatum\n\nred = rot\n\nremcat = Entferne ungenutzte Kategorien|R\n\nreminder_time = Zeitpunkt f\\u00FCr Emailerinnerung|R\n\nrepeating = Wiederholung\n\nreset_state_warning = Die pers\\u00F6nlichen Definitionen f\\u00FCr Aufgabentypen und -stati Einstellungen gel\\u00F6scht.\\\\nAlle Definitionen werden in die Grundeinstellung zur\\u00FCckgesetzt.\\\\nSind Sie sicher?\\\\n\n\nrestore_defaults = Standardwerte wiederherstellen\n\nrlsnotes = Release Notes\n\nsave_Def = Als Standard speichern\n\nsd_dd_warn = Fehler: Enddatum darf nicht vor dem Startdatum liegen\n\nsd_tip = Speichere aktuelle Werte als Termin-Standard\n\nselect_all = Alle ausw\\u00E4hlen|S\n\nselect_appt = Bitte Termin ausw\\u00E4hlen\n\nselect_export_dir = Export-Verzeichnis w\\u00E4hlen\n\nselectdb = Datenbank ist nicht gesetzt. Bitte setzen Sie Datenbankverzeichnis.\n\nsend_reminder = Sende Erinnerungs-eMail|E\n\nset_def_font = Standardschriftart|D\n\nshow_rpt_num = Zeige Wiederholungs-Nummer|W\n\nshowdoy = Zeige Tage des Jahres an|Y\n\nsocket_port = Socket Port (-1=deaktiviert, Neustart erforderlich)\n\nsocket_warn = Socket Port muss eine Zahl sein\n\nsplash = Startbildschirm anzeigen|S\n\nsrch = Suche|S|F3\n\nstackonerr = Zeige Stack-Trace in Fehlermeldungen|K\n\nstdd_warning = Kann Aufgabe nicht speichern. Das Enddatum einer Unteraufgabe liegt nach dem Enddatum der Aufgabe.\n\nstrike = Durchstreichen\n\nstripecolor = Streifenfarbe der Tabelle\n\nsubject = Betreff\n\nsubtask = Unteraufgabe\n\nsubtask_id = ID Unteraufgaben\n\ntaskOptions = Aufgaben\n\ntask_abbrev = Aufgaben-Nummern im Kalender anzeigen\n\ntaskdd_warning = Es kann keine Aufgabe gespeichert werden, deren Enddatum nach dem des Projekts liegt\n\ntasks = Aufgaben\n\ntodomissingdata = Bitte Text und Datum f\\u00FCr Aufgabe eingeben.\n\ntodoquickentry = Termin-Schnelleintrag *\n\ntotal_tasks = Alle Aufgaben\n\ntruncate_appts = K\\u00FCrze Termine in der Monatsansicht|T\n\ntth = Di-Do\n\nucolortext1 = Benutzerdefinierte Farben in Aufgaben|T\n\nucolortext10 = Feiertage\n\nucolortext11 = Geburtstage\n\nucolortext12 = Standard\n\nucolortext13 = Feiertag\n\nucolortext14 = Halbtags\n\nucolortext15 = Urlaub\n\nucolortext16 = Heute\n\nucolortext17 = Wochenende\n\nucolortext18 = Wochentag\n\nucolortext2 = Markiere Aufgaben in Monatsansicht|M\n\nucolortext4 = Textfarbe 1\n\nucolortext5 = Textfarbe 2\n\nucolortext6 = Textfarbe 3\n\nucolortext7 = Textfarbe 4\n\nucolortext8 = Textfarbe 5\n\nucolortext9 = Aufgaben\n\nuncategorized = <Keine Kategorie>\n\nuser = Nutzer\n\nviewchglog = Change Log anzeigen|V\n\nweekdays = Montag-Freitag\n\nweekends = Samstag und Sonntag\n\nweekly = w\\u00F6chentlich\n\nwhite = wei\\u00DF\n\nwview_font = Wochenansicht\n\nyearly = j\\u00E4hrlich\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_es.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NUEVA CITA *****\n\n-db_argument_is_missing = -db argument is missing\n\nAbout = Acerca de...\n\nAbout_BORG = Sobre BORG\n\nAction = Acci\\u00F3n\n\nAdd = A\\u00F1adir\n\nAddCat = Nueva Categor\\u00EDa\n\nAdd_Log = A\\u00F1adir entrada de diario\n\nAdd_New = A\\u00F1adir Nuevo\n\nAdd_State = A\\u00F1adir Estado\n\nAdd_Type = A\\u00F1adir Tipo\n\nAddress = Direcci\\u00F3n\n\nAddress_Book = Libreta\n\nAddress_Book_Entry = Entrada de Libreta\n\nAfter = Despu\\u00E9s \n\nAll = Todos\n\nAll_Open = Todo Abierto\n\nApply_DB_Change = Applicar Cambio DB\n\nAppointment_Editor = Editor de Citas\n\nAppointment_Editor_for_ = Editar Cita ...\n\nBefore = Antes\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Cumplea\\u00F1os\n\nBoxing_Day = Boxing Day\n\nBrowse = Navegar\n\nBuild_Time\\:_ = \\\\nBuild Time:\\\\n\n\nCLOSED = Cerrado\n\nCanada_Day = D\\u00EDa de Canad\\u00E1\n\nCancel = Cancelar\n\nCategories = Categor\\u00EDas\n\nCategory = Categor\\u00EDa\n\nCell_Phone\\: = Tel\\u00E9fono Celular:\n\nChange = Cambiar\n\nCheckList = lista de verificaci\\u00F3n\n\nCheckList_Name = Nombre de lista de verificaci\\u00F3n\n\nCheckLists = Listas de verificaci\\u00F3n\n\nChristmas = Navidad\n\nCivic_Holiday = Festivo civil\n\nClear = Borrar\n\nClear_DueDate = Borrar Fecha L\\u00EDmite\n\nClone = Clonar\n\nClose = Cerrar\n\nClosed = Cerrado\n\nColor = Color\n\nColumbus_Day = D\\u00EDa de la Hispanidad\n\nCommonwealth_Day = D\\u00EDa de la Commonwealth \n\nCompany = Compa\\u00F1\\u00EDa\n\nConfirm_DB_Change = Confirmar Cambio de DB\n\nConfirm_Delete = Confirmar Borrado\n\nCopy = Copiar\n\nCopy_CheckList = Copiar Lista de verificaci\\u00F3n\n\nCould_not_parse_times\\:_ = No puedo analizar horas:\n\nDataBase_Directory = Directorio de Base de Datos|D\n\nDatabaseInformation = Info Base de Datos\n\nDate = Fecha\n\nDay_View = Vista de D\\u00EDa\n\nDaylight_Savings_Time = Hora de Verano\n\nDays = D\\u00EDas\n\nDays_Left = D\\u00EDas Restantes\n\nDelete = Borrar\n\nDelete_Addresses = \\u00BFBorrar Direcciones?\n\nDelete_CheckList = Borrar Listas de verificaci\\u00F3n\n\nDelete_Memo = Borrar Memo\n\nDelete_One_Only = Borrar Uno Solo|Y\n\nDelete_State = Borrar Estado\n\nDelete_Type = Borrar Tipo\n\nDescription = Descripci\\u00F3n\n\nDirectory_[ = Directorio [\n\nDiscard_Text? = \\u00BFDescartar Texto?\n\nDismiss = Descartar||alt X\n\nDone_(Delete) = Hecho (Borrado)|E\n\nDone_(No_Delete) = Hecho (No Borrado)|O\n\nDue = L\\u00EDmite\n\nDueDate = Restante\n\nDue_Date = Fecha L\\u00EDmite\n\nEdit = Editar\n\nEditNew = Editar Nuevo\n\nEdited_CheckList = La lista de verificaci\\u00F3n actual se ha cambiado. \\u00BFDescartar Cambios?\n\nEdited_Memo = El Memo accual se ha cambiado. \\u00BFDescartar Cambios?\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = Par\\u00E1metros de Email\n\nEnable_Email = Habilitar Email\n\nEncryptOnSave = Encriptar al Guardar\n\nEncryptedItem = <<<Elemento encriptado. Pulsa el Bot\\u00F3n Desencriptar para desencriptarlo >>>\n\nEncryptedItemShort = <<Encriptado>>\n\nEncryption = Encriptar\n\nEndDate = Fecha Final\n\nEnterPasswordToDecrypt = Teclee la clave de encriptaci\\u00F3n:\n\nEnter_CheckList_Name = Teclee el nombre de la nueva lista de verificaci\\u00F3n\n\nEnter_Date = Introducir Fecha\n\nEnter_Log = Introducir entrade de Diario\n\nEnter_Memo_Name = Introducir nombre del nuevo memo\n\nExisting_CheckList = Ya existe una lista de verificaci\\u00F3n con ese nombre\n\nExisting_Memo = Ya exite un memo con ese nombre\n\nExit = Salir\n\nFather's_Day = D\\u00EDa del Padre\n\nFax\\: = Fax:\n\nFilter\\: = Filtro:\n\nFind = Buscar|B\n\nFirst = Primero\n\nFirst_Name\\: = Nombre:\n\nFrequency = Frecuencia\n\nGo_To = Ir A\n\nGround_Hog_Day = D\\u00EDa de la Marmota\n\nHalf_Day = Medio D\\u00EDa\n\nHalloween = Sama\\u00EDn\n\nHelp = Ayuda\n\nHide = Ocultar\n\nHide_Pops = Ocultar todos los Recordatorios\n\nHoliday = Festivo\n\nHomeAddress = Direcci\\u00F3n de Casa\n\nHome_City\\: = Ciudad:\n\nHome_Country\\: = Pa\\u00EDs:\n\nHome_Phone = Tel. Casa\n\nHome_Phone\\: = Tel Casa:\n\nHome_State\\: = Estado:\n\nHome_Street_Address = Direcci\\u00F3n\n\nHome_Zip_Code\\: = Cod Postal:\n\nHour = Hora\n\nHours = Horas\n\nISO_week_number = Usar numeraci\\u00F3n semanal ISO 8601\n\nImport = Importar\n\nImport_WARNING = ADVERTENCIA de importaci\\u00F3n\n\nImporting_ = Importando\n\nIndependence_Day = D\\u00EDa de la Independencia\n\nInitializing = Iniciando\n\nInsert_Above = Insertar Antes\n\nInsert_Below = Insert Despu\\u00C9s\n\nItem = Item\n\nItem_ = Item\n\nItem_\\# = Item #\n\nKeyStore = Archivo de claves\n\nKey_Store_Not_Set = Archivo de claves no establecido. Para usar encriptaci\\u00F3n debe establecerlo en preferencias.\n\nLabor_Day = Dia del Trabajo\n\nLabour_Day_(Can) = D\\u00EDa del Trabajo (Can)\n\nLast = \\u00DAltimo\n\nLast_Name\\: = Apellidos:\n\nLicense = Licencia\n\nLogs = Diario\n\nLook_and_Feel\\: = Apariencia:\n\nMartin_Luther_King_Day = D\\u00EDa de Martin Luther King\n\nMemo_Name = Nombre de Memo\n\nMemorial_Day = Memorial Day\n\nMemos = Memos\n\nMinute = Minuto\n\nMinutes = Minutos\n\nMonth_View = Vista de Mes\n\nMother's_Day = Dia de la Madre\n\nMove_To_Following_Day = Mover al D\\u00EDa Siguiente|M\n\nNEW_Item = Nuevo Item\n\nNew_CheckList = Nueva lista de verificaci\\u00F3n\n\nNew_Memo = Nuevo Memo\n\nNew_State = Nuevo Estado\n\nNew_Task_Type = Nuevo tipo de tarea\n\nNew_Theme_Name = Introduzca un nombre de tema\n\nNew_Year's_Day = Nuevo A\\u00F1o\n\nNext_States = Siguientes estados\n\nNickname\\: = Apodo:\n\nNoOpenState = Error: No se encuentra estado inicial para el tipo de tarea\n\nNo_Key_Store = No puedo leer Archivo de Claves: \n\nNo_Specific_Time = Sin Especif. Tiempo\n\nNo_more = No hay m\\u00E1s recordatorios para este apunte\n\nNot_Found_End = No encontado - fin de la b\\u00FAsqueda...\n\nNotes = Notas\n\nNotice = Aviso\n\nNow = -- Ahora --\n\nOK = OK\n\nOPEN = ABRIR\n\nOpen = Abrir\n\nOpen_Calendar = Abrir Calendario\n\nOptions = Opciones\n\nPA = PA\n\nPager\\: = Busca:\n\nPassword = Clave|P\n\nPasswordsDoNotMatch = Las claves no coinciden. Archivo de Claves no creado.\n\nPlease_choose_File_to_Import_From = Elija fichero desde el cual Importar\n\nPlease_choose_directory_to_place_XML_files = Elija directorio para poner los ficheros XML\n\nPlease_enter_some_appointment_text = Introduzca el texto de la cita\n\nPlease_select_a_state = Elija un estado\n\nPlease_select_a_type = Elija un tipo\n\nPlugins = Plugins\n\nPopup_Times = Tiempos de recordatorio\n\nPresidents_Day = Presidents Day\n\nPri = Pri\n\nPrint = Imprimir\n\nPrint_Chooser = Elegir Impresora\n\nPrint_In_Color? = Imprimir en Color?\n\nPriority = Prioridad\n\nPrivate = Privado\n\nProjectInformation = Informaci\\u00F3n del Proyecto\n\nProperties = Propiedades\n\nQuick_Note = Nota R\\u00E1pida\n\nReally_Delete_ = Borrar Realmente\n\nReally_change_the_database? = Cambiar la base de datos?\n\nReally_delete_number_ = Eliminar el numero\n\nRecurrence = Recurrencia\n\nRemembrance_Day = Remembrance Day\n\nReminder = Recordatorio\n\nReminder_Notice = Aviso de recordatorio BORG \n\nRemove = Borrar\n\nRename_State = Renombrar Estado\n\nRename_Type = Renombrar Tipo\n\nReset_Task_States_to_Default = Resetear Tareas\n\nResolution = Resoluci\\u00F3n\n\nRestart_Warning = Debes reiniciar el programa para hacer efectivos los cambios. El programa finalizar\\u00E1 ahora...\n\nRunSQL = Run SQL\n\nSMTP_Port = Puerto SMTP\n\nSMTP_Server = Servidor SMTP\n\nSMTP_password = Password SMTP (Opcional)\n\nSMTP_user = Usuario SMTP (Opcional)\n\nSave = Guardar\n\nSave_&_Close = Guardar y Cerrar|C|\n\nSave_CheckList = Guardar lista de verificaci\\u00F3n\n\nSave_Memo = Guardar Memo\n\nScreen_Name\\: = Nombre de pantalla:\n\nSearchString = Buscar Cadena\n\nSearch_For = Buscar...\n\nSearch_Next = Buscar Siguiente...\n\nSearch_Results = Resultados B\\u00FAsqueda\n\nSelect = Seleccionar\n\nSelectKeyStore = Elija un Archivo de claves\n\nSelect_CheckList_Warning = Para realizar esta acci\\u00F3n debe seleccionar una lista de verificaci\\u00F3n\n\nSelect_Memo_Warning = Para realizar esta acci\\u00F3n debe seleccionar un memo\n\nSelect_initial_state = Elija estado inicial\n\nSelect_next_state = Elija el siguiente estado\n\nSet_DueDate = Poner Fecha L\\u00EDmite\n\nSet_Initial_State = Poner Estado Inicial\n\nShow_Canadian_Holidays = Ver Festivos CAN\n\nShow_Pops = Ver todos los recordatorios\n\nShow_Private_Appointments = Mostrar Citas Privadas\n\nShow_Public_Appointments = Mostrar Citas P\\u00FAblicas\n\nShow_Stack_Trace = Show Stack Trace\n\nShow_U.S._Holidays = Ver Festivos US\n\nSt._Patrick's_Day = D\\u00EDa de St. Patrick\n\nStandard_Time = Horario de Invierno\n\nStartDate = Fecha Inicio\n\nStartToSysTray = Iniciar en el \\u00E1rea de notificaci\\u00F3n\n\nStart_Date = Fecha de Inicio\n\nStart_Time\\: = Hora Inicio:\n\nState_Change = Cambio de Estado\n\nStates = Estados\n\nStatus = Estado\n\nSubTasks = SubTareas\n\nTaskInformation = Detalles de Tarea\n\nTask_Created = Tarea Creada\n\nTask_State_Editor = Editor de Estado de Tarea\n\nTask_Types = Tipos de Tarea\n\nText = Texto\n\nThanksgiving = Acci\\u00F3n de Gracias\n\nThanksgiving_(Can) = Acci\\u00F3n de Gracias (Can)\n\nTheme = Tema\n\nTime = Hora\n\nTimes = Veces\n\nTo_Do = To-Do\n\nTo_Do_List = Lista To-Do\n\nToday = Hoy\n\nType = Tipo\n\nUncheck_All = Desmarcar Todo\n\nUnhide_All = Ver Todo\n\nUntil = Hasta\n\nUse_24_hour_time_format = Usar formato 24 H\n\nUse_system_beep = Usar campana del Sistema\n\nUserColorScheme = Esquema de color de Usuario\n\nVacation = d\\u00EDa libre\n\nValentine's_Day = San Valentin\n\nVeteran's_Day = D\\u00EDa del Veterano\n\nVictoria_Day = D\\u00EDa de la Victoria\n\nWeb_Page\\: = P\\u00E1gina Web:\n\nWeek_Starts_with_Monday = Semana Empieza en Lunes\n\nWeek_View = Vista Semanal\n\nWeek_View_End_Hour\\:_ = Hora Fin Vista Semanal:\n\nWeek_View_Start_Hour\\:_ = Hora Inicio Vista Semanal:\n\nWholeWord = Palabra Completa\n\nWorkAddress = Direcci\\u00F3n de Trabajo\n\nWork_City\\: = Ciudad:\n\nWork_Country\\: = Pa\\u00EDs:\n\nWork_Phone = Tel. Trabajo\n\nWork_Phone\\: = Tel Trabajo:\n\nWork_State\\: = Estado:\n\nWork_Street_Address = Direcci\\u00F3n\n\nWork_Zip_Code\\: = Cod Postal:\n\nYear_View = Vista de A\\u00F1o\n\nYour_Email_Address = Direcci\\u00F3n de EMail\n\n]_does_not_exist = ] no existe\n\n]_is_not_a_directory = ] no es un directorio\n\n__through__ = hasta\n\naddcat = A\\u00F1adir Nueva Categor\\u00EDa\n\naddresses = Direcciones\n\nall_undos = Mostrar toda la pila de Deshacer (Solo lectura)\n\nappearance = Apariencia\n\napply = Aplicar|A\n\nappointment = Apunte\n\nappointments = Apuntes\n\nappt_error = La ventana de Apuntes se cerrar\\u00E1 por un error previo. Puede que un apunte se haya borrado de la ventana To-Do mientas el editor estaba abierto.  Si no es as\\u00ED reinicia Borg\n\napptlist = Lista de Citas\n\nappttext = Texto del Apunte\n\nappttime = Hora del Apunte\n\natt_folder_err = No puedo crear directorio del Anexo:\n\natt_not_found = No puedo encontrar objeto enlazado\n\natt_owner_null = No puedo guardar el enlance antes de que este nuevo item se guarde en la base de datos\n\nattach_file = Anexar Fichero\n\nbackup_dir = Directorio de Copia de Seguridad\n\nbackup_notice = Copia de Seguridad de los datos a\n\nbad_db_2 = \\\\nSi un directorio de Datos err\\u00F3neo causa el error, click en SI para cambiarlo.\n\nbd_rem_days = D\\u00EDas de recordatorio de Cumplea\\u00F1os (-1 inhabilitar)\n\nbeep_options = Sonido de recordatorio\n\nbiweekly = quincenal\n\nblack = negro\n\nblue = azul\n\nborg_backup = Fichero de copia de seguridad de BORG\n\ncalShowSubtask = Mostrar subtareas en Calendario/To-do\n\ncalShowTask = Mostrar tareas en Calendario/To-do\n\ncase_sensitive = Case Sensitive\n\ncat_choose = Elige Categor\\u00EDa\n\ncatchooser = Elegir Categor\\u00EDa\n\nchangedate = Cambiar Fecha\n\nchg_cat = Cambiar Categor\\u00EDa\n\nchoose_file = Elegir Fichero\n\nchoosecat = Elegir Categor\\u00EDas\n\nclear_all = Limpiar Todo|C\n\nclear_undos = Limpiar Registro de deshacer\n\nclose_date = Close Date\n\nclose_proj_warn = No puedo cerrar proyecto. Al menos una tarea hija est\\u00E1 sin cerrar.\n\nclose_tabs = Cerrar todas las pesta\\u00F1as\n\ncollapse = Colapsar\n\nconfirm_overwrite = Confirma Sobreescritura\n\ncontact = Informaci\\u00F3n de contacto\n\ncontrib = Mohan Embar, Stepan Borodulin, Josie Stauffer, Meianandh, Travis Hein\n\ncontributions_by = Thanks for Contributions to:\n\ncopy_appt = Copiar Apunte\n\ncopyright = Copyright\n\ncreate_key_store = Archivo de Claves no Existe. Escriba una clave para crearlo o pulse Cancelar. Debe escribir la clave dos veces para confirmar.\n\ncreated = Creado\n\ncustom_times_header = Popup Reminder Times for\n\ndaily = diario\n\ndb_set_to = \\\\n\\\\nTu Base de Datos puesta a:\n\ndb_time_error = The database shutdown timestamp is older than the shutdown timestamp in the BORG preferences. It is possible that BORG failed to properly write to the database when last shutdown. Please check for lost data, unless you have been using multiple databases.\n\ndecrypt = Desencriptar\n\ndefault = por defecto\n\ndefaultfg = color por defecto\n\ndel_tip = Borrar todas las ocurrencias (repeticiones) de un apunte\n\ndelcat_warn = AVISO! Borrando todos los Apuntes y Tareas para una categor\\u00EDa\n\ndelete_cat = Borrar Categor\\u00EDa|D\n\ndelete_cat_choose = Elija Categor\\u00EDa para borrar\n\ndelete_selected = Borrar Seleccionado\n\ndeleted = Borrado\n\ndlist = Select Days\n\ndock = Dock\n\ndone = hecho\n\ndoo_tip = Borrar solo una ocurrencia de un evento que se repite.\n\nduration = Duraci\\u00F3n\n\ndview_font = Vista de D\\u00EDa\n\nedit_types = Editar Tipos de Tarea y Estados|E\n\nelapsed_time = Transcurrido\n\nempty_desc = Introduzca una descripci\\u00F3n\n\nenable_popups = Habilitar recordatorio\\n\n\nenable_systray = Habilitar Icono en el \\u00C1rea de Notificaci\\u00F3n (Hay que reiniciar)\n\nenable_tls = Habilitar TLS\n\nenturl = Introducir URL:\n\nep = Editar Preferencias\n\nexit_no_backup = Salir sin crear copia de seguridad\n\nexpXML = Exportar como XML...\n\nexpand = Expandir\n\nexport = Exportar\n\nexport_prefs = Exportar Preferencias\n\nfonts = Tipos de Letra\n\nforever = Repetir\n\ngradient_appts = Dibujar Gradiente de Color en las Cajas de Apuntes\n\ngreen = verde\n\nh2info = H2 Information\n\nhaslinks = Tiene Enlaces\n\nhide_strike = Ocultar Items Tachados\n\nhistory = Historia\n\nhsqldb = HSQLDB\n\nhsqldbinfo = HSQLDB Information\n\nimpXML = Importar desde XML...\n\nimpexpMenu = Importar / Exportar|I\n\nimport_prefs = Importar Preferencias\n\nimport_zip = Importar Copia de Seguridad Zip Completa\n\njdbc = JDBC Gen\\u00E9rico\n\nlink_file = fichero Enlace\n\nlinks = Enlaces\n\nlocale = Locale (requiere reinicio)\n\nmemo = Memo\n\nmin_aft_app = minutos despues de la cita\n\nmin_bef_app = minutos antes de la cita\n\nminute_reminder = minuto recordatorio\n\nminutes_ago = minutos antes!\n\nmisc = Miscel\\u00E1nea\n\nmonthly = mensual (fecha)\n\nmonthly_day = mensual (dia)\n\nmview_font = Letra para Imprimir\n\nmwf = Lun-Mie-Vie\n\nnavy = navy(EN)\n\nndays = Cada N D\\u00EDas\n\nnewDate\\: = Nueva Fecha:\n\nnmonths = Cada N Meses\n\nnoOutput = SQL Command Returned no output or errors\n\nno_sound = sin sonido\n\nno_undos = Nada que Deshacer\n\nnummonths = N\\u00FAmero de meses a Imprimir?\n\nnweeks = Cada N Semanas\n\nnyears = Cada N A\\u00F1os\n\non_shutdown = Al Terminar:\n\nonce = una vez\n\nopen_subtasks = No puedo cerrar Tarea, hay Subtareas sin cerrar\n\nopen_tasks = Abrir Tareas\n\noverwrite_warning = Fichero Existe. OK para sobreescribirlo:\n\nparent = Padre\n\nplease_confirm = Confirme Por Favor\n\npopup_reminders = Recordar con popup\n\nprojchild_warning = No puedo guardar proyecto. Un proyecto hijo tiene fecha l\\u00EDmite posterior a la del proyecto.\n\nprojdd_warning = No puedo guardar proyecto. Una tarea hija tiene fecha l\\u00EDmite posterior a la del proyecto.\n\nproject = Proyecto\n\nproject_tree = \\u00C1rbol de Proyecto\n\nprojects = Proyectos\n\nprojpar_warning = No puedo guardar proyecto. Su fecha l\\u00EDmite es posterior a la del proyecto padre.\n\nprompt_for_backup = Preguntar si se debe hacer copia de seguridad\n\npw_time = Password Expiration (secs):\n\nrecur_compat = Error: La recurrencia no es compatible con fecha de apunte\n\nred = rojo\n\nremcat = Borrar Categor\\u00EDa\n\nreminder_list = Mostra todos los recordatorios en una sola ventana (requiere reinicio)\n\nreminder_time = Hora de recordatorio por Email|R\n\nrepeating = Repetici\\u00F3n\n\nreset_state_warning = Esto resetear\\u00E1 cualquier cambio que haya hecho.\\\\n Esta seguro?\\\\n\n\nrestore_defaults = restablecer valores por defecto\n\nrlsnotes = Notas de liberaci\\u00F3n\n\nsave_Def = Guardar valores por defecto\n\nsd_dd_warn = Error: La Fecha L\\u00EDimite no puede ser menor que la inicial\n\nsd_tip = Guardar valores actuales como nuevos valores por defecto del Apunte\n\nselect_all = Seleccionar Todo|S\n\nselect_appt = Seleccione un Apunte\n\nselect_export_dir = Seleccione un directorio de Exportaci\\u00F3n\n\nselect_link_type = Seleccione un tipo de objeto para el Enlace\n\nselectdb = Base de datos no establecida. Introduzca preferencias de la base de datos.\n\nsend_reminder = Enviar correo recordatorio|E\n\nset_appt_font = Vista de Mes\n\nset_def_font = Fuente por defecto\n\nshow_closed = Ver Proyectos Cerrados\n\nshow_closed_tasks = Ver Tareas Cerradas\n\nshow_date_in_systray = Mostrar fecha en el \\u00E1rea de notificaci\\u00F3n\n\nshow_rpt_num = Show Repeat Number|W\n\nshow_subtasks = Ver SubTareas\n\nshow_task_reminders = Ver Recordatorios para Tareas, Proyectos y Subtareas\n\nshow_task_status_in_tree = Ver N\\u00FAmero de Tarea y Estado el el \\u00C1rbol de Proyecto\n\nshowdoy = Ver D\\u00EDa del a\\u00F1o|Y\n\nshutdown = Shutting Down....Please wait\n\nshutdown_options = Elija opci\\u00F3n de cierre y copia de seguridad\n\nsocket_port = Socket Port (-1=disabled, requires restart)\n\nsocket_warn = Socket Port must be an integer\n\nsort_by_priority = Ordenar Apuntes por Prioridad\n\nsplash = Mostrar Splash\n\nsrch = Buscar\n\nstackonerr = Show Stack Trace Option on Error Dialogs|K\n\nstdd_warning = No puedo guardar tarea. Una subtarea tiene fecha l\\u00EDmite posterior a la de la tarea.\n\nstrike = tachar\n\nstripecolor = color lineas de tabla\n\nstsd_warning = No puedo guardar tarea. Una subtarea tiene fecha inicial anterior a la de la tarea.\n\nsubject = Asunto\n\nsubtask = Subtarea\n\nsubtask_id = ID de Subtarea\n\ntask = Tarea\n\ntaskOptions = Opciones de Tarea\n\ntask_abbrev = Mostrar N\\u00FAmero de Tarea en Calendario\n\ntaskdd_warning = No puedo guardar con fecha l\\u00EDmite posterior a la del proyecto.\n\ntasks = Tareas\n\ntodoOptions = Opciones de To-Do\n\ntodo_option_auto_clear_text = Borrar campo de texto To Do al a\\u00F1adir\n\ntodo_option_auto_date_today = Fecha por defecto hoy\n\ntodo_reminder_freq = Minutos entre recordatorios para to-dos sin hora\n\ntodomissingdata = Introduzca textxo y fecha de To-Do.\n\ntodoquickentry = Entrada To-Do *\n\ntotal_tasks = Total Tareas\n\ntray_bg = Color de fondo del Icono\n\ntray_fg = Color del Icono\n\ntray_font = Letra del Icono\n\ntruncate_appts = Truncar Apuntes en Vista de Mes|T\n\ntth = Tues-Thurs\n\nucolortext1 = Colores de usuario en la vista To-Do|T\n\nucolortext10 = vacaciones\n\nucolortext11 = Cumplea\\u00F1os\n\nucolortext12 = fondo por defecto\n\nucolortext13 = festivo\n\nucolortext14 = medio d\\u00EDa\n\nucolortext15 = d\\u00EDa libre\n\nucolortext16 = hoy\n\nucolortext17 = fin de semana\n\nucolortext18 = d\\u00EDa de semana\n\nucolortext2 = marcar to-do en vista de Mes|M\n\nucolortext4 = color texto 1\n\nucolortext5 = color texto 2\n\nucolortext6 = color texto 3\n\nucolortext7 = color texto 4\n\nucolortext8 = color texto 5\n\nucolortext9 = tareas\n\nuncategorized = <Sin Categor\\u00EDa>\n\nundo = Deshacer\n\nunknown = desconocido\n\nupdated = Actualizado\n\nurl = URL\n\nuser = usuario\n\nviewchglog = Ver Change Log\n\nweekdays = Lunes-Viernes\n\nweekends = Sabado y Domingo\n\nweekly = semanal\n\nwhite = blanco\n\nwrite_backup_file = Copia de seguridad a fichero\n\nwview_font = Vista Semanal\n\nyearly = anual\n\nyview_font = Vista Anual\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_es_AR.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NUEVA CITA *****\n\n-db_argument_is_missing = -falta argumento de BD\n\nAbout = Acerca de...|A\n\nAbout_BORG = Acerca de BORG\n\nAction = Acci\\u00F3n|C\n\nAdd = A\\u00F1adir|A|INSERT\n\nAddCat = Nueva categor\\u00EDa:\n\nAdd_Log = A\\u00F1adir entrada al registro\n\nAdd_New = A\\u00F1adir nuevo|N|INSERT\n\nAdd_State = A\\u00F1adir estado\n\nAdd_Type = A\\u00F1adir tipo\n\nAddress = Direcci\\u00F3n\n\nAddress_Book = Libreta de direcciones|L|ctrl L \n\nAddress_Book_Entry = Entrada de libreta de direcciones\n\nAfter = luego\n\nAll = TODOS/AS\n\nAll_Open = TODAS NO CERRADAS \n\nApply_DB_Change = Applicar cambio BD|C\n\nAppointment_Editor = Editor de citas\n\nAppointment_Editor_for_ = Cita\n\nBefore = antes\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Cumplea\\u00F1os|S\n\nBoxing_Day = Boxing Day\n\nBrowse = Revisar|R\n\nBuild_Time\\:_ = \\\\nBuild Time:\n\nCLOSED = CERRADO\n\nCanada_Day = D\\u00EDa de Canad\\u00E1 \n\nCancel = Cancelar\n\nCategories = Categor\\u00EDas|R\n\nCategory = Categor\\u00EDa\n\nCell_Phone\\: = Tel\\u00E9fono Celular:\n\nChange = Cambiar|M|ctrl E\n\nCheckList = Lista de verificaci\\u00F3n\n\nCheckList_Name = Nombre lista de verificaci\\u00F3n\n\nCheckLists = Listas de verificaci\\u00F3n\n\nChristmas = Navidad\n\nCivic_Holiday = Fiesta c\\u00EDvica\n\nClear = Borrar\n\nClear_DueDate = Borrar fecha vencim.\n\nClone = Clonar|O|ctrl O\n\nClose = Cerrar|R|ctrl X\n\nClosed = Cerrado|R\n\nColor = Color|L\n\nColumbus_Day = 12 de octubre\n\nCommonwealth_Day = D\\u00EDa del Commonwealth\n\nCompany = Compa\\u00F1\\u00EDa|C\n\nConfirm_DB_Change = Confirmar cambio BD\n\nConfirm_Delete = Confirmar borrado\n\nCopy = Copiar\n\nCopy_CheckList = Copiar lista de verificaci\\u00F3n\n\nCould_not_parse_times\\:_ = No se pudo analizar repeticiones:\n\nDataBase_Directory = Directorio base de datos|D\n\nDatabaseInformation = Info base de datos\n\nDate = Fecha|F\n\nDay_View = Vista diaria\n\nDaylight_Savings_Time = Horario de verano\n\nDays = D\\u00EDas\n\nDays_Left = D\\u00EDas restantes\n\nDelete = Borrar|B|DELETE \n\nDelete_Addresses = Borrar direcci\\u00F3n(es)?\n\nDelete_CheckList = Borrar lista de verificaci\\u00F3n\n\nDelete_Memo = Borrar memo\n\nDelete_One_Only = Borrar ocurrencia actual|O\n\nDelete_State = Borrar estado\n\nDelete_Type = Borrar tipo\n\nDescription = Descripci\\u00F3n\n\nDirectory_[ = Directorio [\n\nDiscard_Text? = Descartar texto?\n\nDismiss = Cerrar||alt X\n\nDone_(Delete) = Hecho (borrar)|B\n\nDone_(No_Delete) = Hecho (no borrar)|O\n\nDue = Vencimiento\n\nDueDate = Fecha vencim. \n\nDue_Date = Fecha vencim.|V\n\nEdit = Editar|E\n\nEditNew = Editar nuevo\n\nEdited_CheckList = La lista de verificaci\\u00F3n se modific\\u00F3. Descartar cambios?\n\nEdited_Memo = El memo actual ha cambiado. Descartar cambios?\n\nEmail = Email\n\nEmail\\: = Email:|E\n\nEmailParameters = Par\\u00E1metros email\n\nEnable_Email = Habilitar email|E\n\nEncryptOnSave = Encriptar al guardar\n\nEncryptedItem = <<<Item encriptado. Presione el bot\\u00F3n Desencriptar para desencriptarlo>>>\n\nEncryptedItemShort = <<Encriptado>>\n\nEncryption = Encriptaci\\u00F3n\n\nEndDate = Fecha fin\n\nEnterPasswordToDecrypt = Clave de encriptaci\\u00F3n:\n\nEnter_CheckList_Name = Entrar nombre de nueva lista de verificaci\\u00F3n\n\nEnter_Date = Entrar fecha\n\nEnter_Log = Ingresar entrada de registro\n\nEnter_Memo_Name = Entre nombre para el nuevo Memo\n\nExisting_CheckList = Existe una lista de verificaci\\u00F3n con ese nombre\n\nExisting_Memo = Ya existe un Memo con ese nombre\n\nExit = Salir|S|alt X\n\nFather's_Day = D\\u00EDa del Padre\n\nFax\\: = Fax:|A\n\nFilter\\: = Filtro:|T\n\nFind = Buscar|B\n\nFirst = Nombre\n\nFirst_Name\\: = Nombre:|N\n\nFrequency = Frecuencia\n\nGo_To = Ir a|I\n\nGround_Hog_Day = Ground Hog Day\n\nHalf_Day = Medio d\\u00EDa\n\nHalloween = Halloween\n\nHelp = Ayuda|Y|F1\n\nHide = Ocultar\n\nHide_Pops = Ocultar recordatorios\n\nHoliday = Feriado\n\nHomeAddress = Direcci\\u00F3n personal\n\nHome_City\\: = Ciudad:|C\n\nHome_Country\\: = Pa\\u00EDs:|P\n\nHome_Phone = Tel. casa\n\nHome_Phone\\: = Tel casa:|T\n\nHome_State\\: = Estado/Prov.:|E\n\nHome_Street_Address = Direcci\\u00F3n (calle)|D\n\nHome_Zip_Code\\: = C\\u00F3d. postal:|T\n\nHour = hora\n\nHours = horas\n\nISO_week_number = Use numeraci\\u00F3n de semanas ISO 8601\n\nImport = Importar\n\nImport_WARNING = ADVERTENCIA de importaci\\u00F3n\n\nImporting_ = Importando\n\nIndependence_Day = Independence Day\n\nInitializing = Iniciando\n\nInsert_Above = Insertar arriba\n\nInsert_Below = Insertar abajo\n\nItem = Item\n\nItem_ = Item \n\nItem_\\# = Item #|I\n\nKeyStore = Almac. claves\n\nKey_Store_Not_Set = Ubicaci\\u00F3n almac. claves no definida. Para usar encriptaci\\u00F3n, definirla en las opciones.\n\nLabor_Day = D\\u00EDa del Trabajo\n\nLabour_Day_(Can) = D\\u00EDa del Trabajo (Can)\n\nLast = Apellido\n\nLast_Name\\: = Apellido:|L\n\nLicense = Licencia|L\n\nLogs = Registros\n\nLook_and_Feel\\: = Apariencia:|N\n\nMartin_Luther_King_Day = D\\u00EDa de Martin Luther King\n\nMemo_Name = Nombre Memo\n\nMemorial_Day = Memorial Day\n\nMemos = Memos\n\nMinute = minuto\n\nMinutes = minutos\n\nMonth_View = Vista mensual\n\nMother's_Day = D\\u00EDa de la Madre\n\nMove_To_Following_Day = Mover al d\\u00EDa siguiente\n\nNEW_Item = Nuevo Item\n\nNew_CheckList = Nueva lista de verificaci\\u00F3n\n\nNew_Memo = Nuevo memo\n\nNew_State = Nuevo estado\n\nNew_Task_Type = Nuevo tipo de tarea\n\nNew_Year's_Day = A\\u00F1o Nuevo\n\nNext_States = Sig. estados\n\nNickname\\: = Apodo:|O\n\nNoOpenState = Error: no hay estado inicial para el tipo de tarea\n\nNo_Key_Store = No se pudo leer almac. claves: \n\nNo_Specific_Time = Sin hora espec\\u00EDfica|P\n\nNo_more = No seguir record\\u00E1ndome esta cita\n\nNot_Found_End = No encontrado ? Fin b\\u00FAsqueda...\n\nNotes = Notas\n\nNotice = Aviso\n\nNow = -- La cita es ahora --\n\nOK = OK\n\nOPEN = ABIERTO\n\nOpen = Abrir|R\n\nOpen_Calendar = Abrir calendario|C\n\nOptions = Opciones|O|ctrl O\n\nPA = PA|A\n\nPager\\: = Pager:|P\n\nPassword = Clave acc.|L\n\nPasswordsDoNotMatch = Las claves no coinciden - Almac. claves no creado\n\nPlease_choose_File_to_Import_From = Elija archivo desde el cual importar\n\nPlease_choose_directory_to_place_XML_files = Elija directorio para archivos XML\n\nPlease_enter_some_appointment_text = Introduzca el texto de la cita\n\nPlease_select_a_state = Elija un estado\n\nPlease_select_a_type = Elija un tipo\n\nPlugins = Plugins\n\nPopup_Times = Tiempos popup\n\nPresidents_Day = Presidents Day\n\nPri = Pri|P\n\nPrint = Imprimir|P|ctrl P \n\nPrint_Chooser = Elecci\\u00F3n impresora\n\nPrint_In_Color? = Imprimir en color?\n\nPriority = Prioridad\n\nPrivate = Privado|V\n\nProjectInformation = Informaci\\u00F3n Proyecto\n\nProperties = Propiedades\n\nQuick_Note = Nota r\\u00E1pida\n\nReally_Delete_ = Borrar realmente\n\nReally_change_the_database? = Cambiar base de datos?\n\nReally_delete_number_ = Realmente eliminar n\\u00FAmero\n\nRecurrence = Recurrencia\n\nRemembrance_Day = Remembrance Day\n\nReminder = Recordatorio\n\nReminder_Notice = Aviso recordatorio de BORG\n\nRemove = Remover\n\nRename_State = Renombrar estado\n\nRename_Type = Renombrar tipo\n\nReset_Task_States_to_Default = Reinicializar estados de tareas|R\n\nResolution = Resoluci\\u00F3n\n\nRestart_Warning = Debe reiniciar el programa para aplicar los cambios. El programa se cerrar\\u00E1 ahora...\n\nRunSQL = Ejecutar SQL\n\nSMTP_Port = Puerto SMTP\n\nSMTP_Server = Servidor SMTP|S\n\nSMTP_password = Clave SMTP(opcional)\n\nSMTP_user = Usuario SMTP(opcional)\n\nSave = Guardar|U|ctrl S\n\nSave_&_Close = Guardar y cerrar\n\nSave_CheckList = Guardar lista de verificaci\\u00F3n\n\nSave_Memo = Guardar Memo\n\nScreen_Name\\: = Nombre pantalla:|M\n\nSearchString = Texto de b\\u00FAsqueda\n\nSearch_For = Buscar...\n\nSearch_Next = Buscar pr\\u00F3ximo...\n\nSearch_Results = Resultados b\\u00FAsqueda\n\nSelect = Seleccionar\n\nSelectKeyStore = Seleccionar arch. almac. claves\n\nSelect_CheckList_Warning = Una lista de verif. debe estar seleccionada para realizar esta acci\\u00F3n\n\nSelect_Memo_Warning = Un memo debe estar seleccionado para realizar esta acci\\u00F3n\n\nSelect_initial_state = Elegir estado inicial\n\nSelect_next_state = Elegir estado pr\\u00F3ximo\n\nSet_DueDate = Establecer fecha venc.\n\nSet_Initial_State = Establecer estado inicial\n\nShow_Canadian_Holidays = Ver feriados canadienses\n\nShow_Pops = Mostrar todos los recordatorios\n\nShow_Private_Appointments = Mostrar citas privadas|V\n\nShow_Public_Appointments = Mostrar citas p\\u00FAblicas|P\n\nShow_Stack_Trace = Mostrar seguimiento pila\n\nShow_U.S._Holidays = Ver feriados USA|U\n\nSt._Patrick's_Day = D\\u00EDa de San Patricio\n\nStandard_Time = Standard Time\n\nStartDate = Fecha inicio\n\nStartToSysTray = Iniciar en bandeja de sistema\n\nStart_Date = Fecha inicio|F\n\nStart_Time\\: = Hora inicio:|N\n\nState_Change = Cambio estado\n\nStates = Estados\n\nStatus = Estatus|U\n\nSubTasks = Subtareas\n\nTaskInformation = Info. de tarea\n\nTask_Created = Tarea creada\n\nTask_State_Editor = Editor de estado de tarea\n\nTask_Types = Tipos de tareas\n\nText = Texto\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = Hora\n\nTimes = veces\n\nTo_Do = Para hacer|H|ctrl H\n\nTo_Do_List = Lista para hacer|L\n\nToday = Hoy|H|HOME\n\nType = Tipo|P\n\nUncheck_All = Desmarcar todo\n\nUnhide_All = Mostrar todo\n\nUntil = Hasta\n\nUse_24_hour_time_format = Usar formato 24 Hs|2\n\nUse_system_beep = Usar sonido de sistema\n\nUserColorScheme = Esquema de color de usuario\n\nVacation = Vacaci\\u00F3n\n\nValentine's_Day = D\\u00EDa de San Valent\\u00EDn\n\nVeteran's_Day = D\\u00EDa del Veterano\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = P\\u00E1g. Web:|G\n\nWeek_Starts_with_Monday = Semana empieza en lunes|L\n\nWeek_View = Vista semanal\n\nWeek_View_End_Hour\\:_ = Hora fin del d\\u00EDa:|F\n\nWeek_View_Start_Hour\\:_ = Hora inicio del d\\u00EDa:|I\n\nWholeWord = Palabra completa\n\nWorkAddress = Direcci\\u00F3n laboral\n\nWork_City\\: = Ciudad:|C\n\nWork_Country\\: = Pa\\u00EDs:|P\n\nWork_Phone = Tel. laboral\n\nWork_Phone\\: = Tel. laboral:|L\n\nWork_State\\: = Estado/prov.:|E\n\nWork_Street_Address = Direcci\\u00F3n (calle)|D\n\nWork_Zip_Code\\: = C\\u00F3d. Postal (trabajo):|S\n\nYear_View = Vista anual\n\nYour_Email_Address = Su dir. de email|E\n\n]_does_not_exist = ] no existe\n\n]_is_not_a_directory = ] no es un directorio\n\n__through__ = hasta\n\naddcat = A\\u00F1adir nueva categor\\u00EDa|A\n\naddresses = Direcciones\n\nall_undos = Ver lista Deshacer (solo lectura)\n\nappearance = Apariencia\n\napply = Aplicar|A\n\nappointment = Cita\n\nappointments = Citas\n\nappt_error = La ventana de cita se cerrar\\u00E1 por un error previo. Quiz\\u00E1 una cita fue quitada de la ventana Para Hacer cuando el editor estaba abierto. Si no, reinicie Borg\n\napptlist = Lista citas\n\nappttext = Texto cita\n\nappttime = Hora cita\n\natt_folder_err = Imposible crear carpeta adj.:\n\natt_not_found = Imposible hallar objeto enlazado\n\natt_owner_null = No se puede guardar enlace antes de guardar el item en la BD\n\nattach_file = Arch. adjunto\n\nbackup_dir = Carpeta backup\n\nbackup_notice = Escriba datos backup a\n\nbad_db_2 = \\\\nSi un directorio de BD err\\u00F3neo causa el error, click en OK para cambiarlo.\n\nbeep_options = Sonido recordatorio\n\nbiweekly = quincenal\n\nblack = negro\n\nblue = azul\n\nborg_backup = Arch. backup de BORG\n\ncalShowSubtask = Mostrar subtareas en Calendario/ParaHacer\n\ncalShowTask = Mostrar tareas en Calendario/ParaHacer\n\ncase_sensitive = Disting. may./min.\n\ncat_choose = Elegir categor\\u00EDa\n\ncatchooser = Selec. categor\\u00EDa\n\nchangedate = Cambiar fecha|M\n\nchg_cat = Cambiar categor\\u00EDa\n\nchoose_file = Elegir archivo\n\nchoosecat = Elegir categor\\u00EDas para mostrar|C\n\nclear_all = Borrar todo|B\n\nclear_undos = Borrar registro deshacer\n\nclose_date = Fecha cierre\n\nclose_proj_warn = Imposible cerrar proyecto. Hay tareas hijas abiertas.\n\nclose_tabs = Cerrar todas las fichas\n\ncollapse = Colapsar\n\nconfirm_overwrite = Confirma sobreescritura\n\ncontact = Info. Contacto\n\ncontributions_by = Gracias por las contribuciones a:\n\ncopy_appt = Copiar cita\n\ncopyright = Copyright\n\ncreate_key_store = No existe archivo almac. claves. Entre una palabra clave para crear un nuevo archivo, o cancele. Debe entrar la palabra clave dos veces.\n\ncreated = Creado\n\ncustom_times_header = Tiempos recordatorio popup para\n\ndaily = diario\n\ndb_set_to = \\\\n\\\\nSu dir. de DB se establece en:\n\ndecrypt = Desencriptar\n\ndefault = defecto\n\ndel_tip = Borrar todas las ocurrencias (repeticiones) de una cita\n\ndelcat_warn = ADVERTENCIA ? Borrando TODAS las citas y tareas de la categor\\u00EDa\n\ndelete_cat = Borrar categor\\u00EDa|B\n\ndelete_cat_choose = Elegir categor\\u00EDa para borrar\n\ndelete_selected = Borrar seleccionados/as\n\ndeleted = Borrado\n\ndlist = Selecionar d\\u00EDas\n\ndock = Organizar ventana\n\ndone = hecho\n\ndoo_tip = Borrar s\\u00F3lo la ocurrencia actual de una cita repetida\n\nduration = Duraci\\u00F3n\n\ndview_font = Vista diaria\n\nedit_types = Editar tipos y estados de tareas|E\n\nelapsed_time = Transcurrido\n\nempty_desc = Entre una descripci\\u00F3n\n\nenable_popups = Habilitar popups recordatorios\n\nenable_systray = Habilitar \\u00EDcono bandeja de sistema (requiere reinicio)\n\nenable_tls = Habilitar TLS\n\nenturl = Entrar URL:\n\nep = Editar preferencias|E\n\nexit_no_backup = Salir sin crear arch. backup\n\nexpXML = Exportar XML|E\n\nexpand = Expandir\n\nexport = Exportar\n\nexport_prefs = Exportar preferencias|X\n\nfonts = Fuentes\n\nforever = Repetir siempre\n\ngradient_appts = dibujar gradiente de color en cajas de citas|D\n\ngreen = verde\n\nh2info = Informaci\\u00F3n H2\n\nhaslinks = Tiene enlaces\n\nhide_strike = Ocultar items tachados\n\nhistory = Historia\n\nhsqldb = HSQLDB\n\nhsqldbinfo = Informaci\\u00F3n HSQLDB\n\nimpXML = Importar XML|I\n\nimpexpMenu = Importar/Exportar|I\n\nimport_prefs = Importar preferencias|I\n\nimport_zip = Import. arch. Zip backup completo\n\njdbc = Generic JDBC\n\nlink_file = Arch. enlaces\n\nlinks = Enlaces\n\nlocale = Idioma y pa\\u00EDs (requiere reinicio)\n\nmemo = Memo\n\nmin_aft_app = minutos luego de la cita\n\nmin_bef_app = minutos antes de la cita\n\nminute_reminder = minutos para la cita\n\nminutes_ago = minutos pasaron desde la cita!\n\nmisc = Varios\n\nmonthly = mensual (fecha)\n\nmonthly_day = mensual (dia)\n\nmview_font = Impr. mes\n\nmwf = lun-mie-vie\n\nnavy = navy(EN)\n\nndays = Cada N d\\u00EDas\n\nnewDate\\: = Nueva fecha:\n\nnoOutput = El comando SQL no trae salida o trae errores\n\nno_sound = sin sonido\n\nno_undos = Nada para deshacer\n\nnummonths = N\\u00FAmero de meses a imprimir?\n\non_shutdown = Al apagar:\n\nonce = una vez\n\nopen_subtasks = Imposible cerrar tarea, hay subtareas abiertas\n\nopen_tasks = Tareas abiertas\n\noverwrite_warning = El arch. existe. OK para reescribirlo:\n\nparent = Padre\n\nplease_confirm = Por favor confirme\n\npopup_reminders = Recordatorios popup\n\nprojchild_warning = Imposible guardar proyecto. Un proyecto hijo tiene fecha venc. posterior a la del padre.\n\nprojdd_warning = Imposible guardar proyecto. Una tarea hija tiene fecha venc. posterior a la del proyecto.\n\nproject = Proyecto\n\nproject_tree = Arbol de proyectos\n\nprojects = Proyectos\n\nprojpar_warning = Imposible guardar proyecto. Fecha de venc. posterior a la del proyecto padre.\n\nprompt_for_backup = Sugerir backup\n\npw_time = Expiraci\\u00F3n pal. clave (seg.):\n\nrecur_compat = Error: recurrencia incompatible con fecha de cita\n\nred = rojo\n\nremcat = Borrar categor\\u00EDas no usadas|R\n\nreminder_list = Ver todos los recordatorios en una ventana (requiere reinicio)\n\nreminder_time = Tiempo recordatorio email|R\n\nrepeating = Repetida\n\nreset_state_warning = Se_perder\\u00E1n las personalizaciones que Ud. haya hecho al modelo de tarea\\\\nEl modelo de tarea ser\\u00E1 el que trae BORG por defecto.\\\\nContinuar?\\\\n\n\nrestore_defaults = restaurar valores por defecto\n\nrlsnotes = Notas de versi\\u00F3n\n\nsave_Def = Guardar valores por def.\n\nsd_dd_warn = Error: fecha de venc. anterior a la de inicio\n\nsd_tip = Guardar valores actuales como valores por defecto para citas\n\nselect_all = Selecionar todo|S\n\nselect_appt = Selecione una cita\n\nselect_export_dir = Elija directorio de export.\n\nselect_link_type = Elija tipo de objeto para enlace\n\nselectdb = Base de datos no establecida. Introducir los ajustes de la BD.\n\nsend_reminder = Enviar email recordatorio|E\n\nset_appt_font = Vista mensual\n\nset_def_font = Fuente por defecto\n\nshow_closed = Mostrar proyectos cerrados\n\nshow_closed_tasks = Mostrar tareas cerradas\n\nshow_date_in_systray = Mostrar fecha en bandeja de sistema\n\nshow_rpt_num = Mostrar n\\u00FAm. repet.\n\nshow_subtasks = Mostrar subtareas\n\nshow_task_status_in_tree = Mostrar no./estado de tarea en \\u00E1rbol de tareas\n\nshowdoy = Mostrar d\\u00EDa del a\\u00F1o\n\nshutdown = Cerrando... Espere por favor\n\nshutdown_options = Selecione opci\\u00F3\\u00B4n de cierre y backup\n\nsocket_port = Socket Port (-1=deshabilitado, requiere reinicio)\n\nsocket_warn = Socket Port debe ser un entero\n\nsort_by_priority = Ordenar citas por prioridad\n\nsplash = Mostrar pantalla bienvenida|S\n\nsrch = Buscar|S|F3\n\nstackonerr = Mostrar opci\\u00F3n seguimiento de pila en di\\u00E1logos de error\n\nstdd_warning = Imposible guardar tarea. Hay subtareas con fecha de venc. posterior a la de la tarea\n\nstrike = tachado\n\nstripecolor = Table Stripe Color\n\nstsd_warning = Imposible guardar tarea. Hay subtareas con fecha de inicio anterior a la de la tarea\n\nsubject = Tema\n\nsubtask = Subtarea\n\nsubtask_id = ID Subtarea \n\ntask = Tarea\n\ntaskOptions = Opciones de tarea\n\ntask_abbrev = Mostrar nos. de tarea en Calendario\n\ntaskdd_warning = Imposible guardar tarea con fecha de vencim. posterior a la del proyecto\n\ntasks = Tareas\n\ntodoOptions = Opciones 'para hacer'\n\ntodo_option_auto_clear_text = Borrar texto campo 'para hacer' al agregar\n\ntodo_option_auto_date_today = D\\u00EDa por defecto: hoy\n\ntodo_reminder_freq = Minutos entre recordatorios de 'para hacer' sin hora\n\ntodomissingdata = Escriba texto y fecha de 'para-hacer'.\n\ntodoquickentry = Entrada r\\u00E1pida 'para-hacer'\n\ntotal_tasks = Tareas totales\n\ntruncate_appts = Truncar citas en vista mensual|T\n\ntth = mar-jue\n\nucolortext1 = use colores de usuario en lista ParaHacer|H\n\nucolortext10 = feriados\n\nucolortext11 = cumplea\\u00F1os\n\nucolortext12 = defecto\n\nucolortext13 = feriado\n\nucolortext14 = medio d\\u00EDa\n\nucolortext15 = vacaci\\u00F3n\n\nucolortext16 = hoy\n\nucolortext17 = fin de semana\n\nucolortext18 = d\\u00EDa de la sem.\n\nucolortext2 = marcar ParaHacer en vista mens.|M\n\nucolortext4 = color texto 1\n\nucolortext5 = color texto 2\n\nucolortext6 = color texto 3\n\nucolortext7 = color texto 4\n\nucolortext8 = color texto 5\n\nucolortext9 = tareas\n\nuncategorized = <No categorizado>\n\nundo = Deshacer\n\nunknown = desconocido\n\nupdated = Actualizado/a\n\nurl = URL\n\nuser = usuario\n\nviewchglog = Registro de cambios|V\n\nweekdays = lunes-viernes\n\nweekends = s\\u00E1bado-domingo\n\nweekly = semanal\n\nwhite = blanco\n\nwrite_backup_file = Backup a un arch.\n\nwview_font = Vista semanal\n\nyearly = anual\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_fr.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NOUVEAU RDV *****\n\n-db_argument_is_missing = l'argument -db manque\n\nAbout = A propos|A\n\nAbout_BORG = A propos de BORG\n\nAction = Action|C\n\nAdd = Ajouter|A|INSERT\n\nAddCat = Entrer Nouvelle Cat\\u00E9gorie:\n\nAdd_Log = Ajouter Entr\\u00E9e Log\n\nAdd_New = Ajouter Nouveau|N|INSERT\n\nAdd_State = Ajouter Etat\n\nAdd_Type = Ajouter Type\n\nAddress = Adresse\n\nAddress_Book = Carnet d'adresses|A|ctrl A\n\nAddress_Book_Entry = Entr\\u00E9e Carnet d'adresses\n\nAfter = Apr\\u00E8s\n\nAll = Tout|A\n\nAll_Open = Tout Ouvrir\n\nApply_DB_Change = Appliquer Changement DB|C\n\nAppointment_Editor = Editeur de Rendez-vous\n\nAppointment_Editor_for_ = Editeur de Rendez-vous pour\n\nBefore = Avant\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Anniversaire|B\n\nBoxing_Day = Boxing Day\n\nBrowse = Explorer|B\n\nBuild_Time\\:_ = \\\\nBuild Time:\n\nCLOSED = CLOSED\n\nCanada_Day = Canada Day\n\nCancel = Annuler\n\nCategories = Cat\\u00E9gories|R\n\nCategory = Cat\\u00E9gorie|R\n\nCell_Phone\\: = T\\u00E9l\\u00E9phone Cellulaire:\n\nChange = Changer|G|ctrl E\n\nChristmas = No\\u00EBl\n\nCivic_Holiday = Civic Holiday\n\nClear = Effacer\n\nClear_DueDate = Effacer Ech\\u00E9ance\n\nClone = Cloner|O|ctrl O\n\nClose = Fermer|L|ctrl X\n\nClosed = Ferm\\u00E9|L\n\nColor = Couleur|O\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = Soci\\u00E9t\\u00E9|C\n\nConfirm_DB_Change = Confirmer Changement DB\n\nConfirm_Delete = Confirmer Suppression\n\nCopy = Copier\n\nCould_not_parse_times\\:_ = Impossible de parser les heures:\n\nDataBase_Directory = R\\u00E9pertoire Base de donn\\u00E9es|D\n\nDatabaseInformation = Information Base de donn\\u00E9es\n\nDate = Date|D\n\nDay_View = Vue Journali\\u00E8re\n\nDaylight_Savings_Time = Daylight Savings Time\n\nDays = Jours\n\nDays_Left = Jours Restants\n\nDelete = Supprimer|D|DELETE\n\nDelete_Addresses = Supprimer Adresse(s)?\n\nDelete_Memo = Supprimer Memo\n\nDelete_One_Only = Supprimer Seulement Un|Y\n\nDelete_State = Supprimer Etat\n\nDelete_Type = Supprimer Type\n\nDescription = Description\n\nDirectory_[ = R\\u00E9pertoire [\n\nDiscard_Text? = Rejeter Texte?\n\nDismiss = Annuler||alt X\n\nDone_(Delete) = Fait (Supprimer)|E\n\nDone_(No_Delete) = Fait (Ne pas Supprimer)|O\n\nDueDate = Ech\\u00E9ance\n\nDue_Date = Ech\\u00E9ance|T\n\nEdit = Editer|E\n\nEditNew = Editer Nouveau\n\nEdited_Memo = Memo en cours a \\u00E9t\\u00E9 modifi\\u00E9. Rejeter Changements?\n\nEmail = Email\n\nEmail\\: = Email:|E\n\nEmailParameters = Param\\u00E8tres Email\n\nEnable_Email = Activer Email|E\n\nEndDate = Date de Fin\n\nEnter_Log = Entrer Log\n\nEnter_Memo_Name = Svp entrer le nom du nouveau memo\n\nExisting_Memo = Un memo existe d\\u00E9j\\u00E0 avec ce nom\n\nExit = Exit|X|alt X\n\nFather's_Day = Father's Day\n\nFax\\: = Fax:|A\n\nFilter\\: = Filtre:|T\n\nFirst = Pr\\u00E9nom\n\nFirst_Name\\: = Pr\\u00E9nom:|F\n\nFrequency = Fr\\u00E9quence|Q\n\nGo_To = Aller A|G\n\nGround_Hog_Day = Ground Hog Day\n\nHalf_Day = Demi-Journ\\u00E9e|L\n\nHalloween = Halloween\n\nHelp = Aide|H|F1\n\nHide_Pops = Masquer Tous les Memos\n\nHoliday = Vacance|I\n\nHomeAddress = Adresse Domicile\n\nHome_City\\: = Ville:|C\n\nHome_Country\\: = Pays:|U\n\nHome_Phone = T\\u00E9l\\u00E9phone Domicile\n\nHome_Phone\\: = T\\u00E9l\\u00E9phone Domicile:|H\n\nHome_State\\: = Etat:|T\n\nHome_Street_Address = Rue|A\n\nHome_Zip_Code\\: = Code:|Z\n\nHour = Heure\n\nHours = Heures\n\nISO_week_number = Utiliser Num\\u00E9rotation ISO 8601 de la semaine\n\nImport = Importer\n\nImport_WARNING = Importer WARNING\n\nImporting_ = Importing\n\nIndependence_Day = Independence Day\n\nInitializing = Initializing\n\nItem = Item\n\nItem_ = Item\n\nItem_\\# = Item #|I\n\nLabor_Day = Labor Day\n\nLabour_Day_(Can) = Labour Day (Can)\n\nLast = Nom\n\nLast_Name\\: = Nom:|L\n\nLicense = Licence|L\n\nLogs = Logs\n\nLook_and_Feel\\: = Look and Feel:|L\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemo_Name = Nom Memo\n\nMemorial_Day = Memorial Day\n\nMemos = Memos\n\nMinute = Minute\n\nMinutes = Minutes\n\nMonth_View = Vue Mensuelle\n\nMother's_Day = Mother's Day\n\nMove_To_Following_Day = D\\u00E9placer au jour suivant|M\n\nNEW_Item = Nouvel Item\n\nNew_Memo = Nouveau Memo\n\nNew_State = Nouvel Etat\n\nNew_Task_Type = Nouveau Type T\\u00E2che\n\nNew_Year's_Day = Nouveau Jour de l'Ann\\u00E9e\n\nNext_States = Etats Suivants\n\nNickname\\: = Surnom:|N\n\nNoOpenState = Erreur: Pas d'\\u00E9tat initial trouv\\u00E9 pour le type t\\u00E2che.\n\nNo_Specific_Time = Pas d'heure sp\\u00E9cifique|P\n\nNo_more = Plus de rappels pour ce rendez-vous\n\nNotes = Notes\n\nNotice = Notice\n\nNow = -- Maintenant! --\n\nOK = OK\n\nOPEN = OPEN\n\nOpen = Ouvrir|N\n\nOpen_Calendar = Ouvrir Calendrier|C\n\nOptions = Options|O|ctrl O\n\nPA = PA|A\n\nPager\\: = Pager:|P\n\nPassword = Mot de Passe|P\n\nPlease_choose_File_to_Import_From = Svp choisir le Fichier d'o\\u00F9 Importer\n\nPlease_choose_directory_to_place_XML_files = Svp choisir le r\\u00E9pertoire o\\u00F9 placer les fichiers XML\n\nPlease_enter_some_appointment_text = Svp entrer du texte pour le rendez-vous\n\nPlease_select_a_state = Svp s\\u00E9lectionner un \\u00E9tat\n\nPlease_select_a_type = Svp s\\u00E9lectionner un type\n\nPopup_Times = Heures Popup\n\nPresidents_Day = Presidents Day\n\nPri = Pri|P\n\nPrint = Imprimer|P|ctrl P\n\nPrint_Chooser = Choisir Imprimante\n\nPrint_In_Color? = Couleur d'impression?|C\n\nPrivate = Priv\\u00E9|V\n\nProjectInformation = Information Projet\n\nProperties = Propri\\u00E9t\\u00E9s\n\nReally_Delete_ = Vraiment Supprimer\n\nReally_change_the_database? = Vraiment changer la base de donn\\u00E9es?\n\nReally_delete_number_ = Vraiment supprimer #\n\nRecurrence = R\\u00E9currence\n\nRemembrance_Day = Remembrance Day\n\nReminder = Rappel\n\nReminder_Notice = BORG Rappel Notice\n\nRename_State = Renommer Etat\n\nRename_Type = Renommer Type\n\nReset_Task_States_to_Default = Re-initialiser Etats T\\u00E2che par D\\u00E9faut|R\n\nResolution = R\\u00E9solution\n\nRunSQL = Ex\\u00E9cuter SQL\n\nSMTP_Port = Port SMTP\n\nSMTP_Server = Serveur SMTP|S\n\nSMTP_password = Mot de passe SMTP (Optionel)\n\nSMTP_user = Utilisateur SMTP (Optionel)\n\nSave = Sauvegarder|S|ctrl S\n\nSave_Memo = Sauvegarder Memo\n\nScreen_Name\\: = Nom Ecran:|M\n\nSearchString = Rechercher Cha\\u00EEne de caract\\u00E8res\n\nSearch_Results = R\\u00E9sultats de la Recherche\n\nSelect = S\\u00E9lectionner\n\nSelect_Memo_Warning = Un memo doit \\u00EAtre s\\u00E9lectionn\\u00E9 pour effectuer cette action\n\nSelect_initial_state = S\\u00E9lectionner Etat Initial\n\nSelect_next_state = S\\u00E9lectionner \\u00E9tat suivant\n\nSet_DueDate = Indiquer Ech\\u00E9ance\n\nSet_Initial_State = Configurer Etat Initial\n\nShow_Canadian_Holidays = Show Canadian Holidays|C\n\nShow_Pops = Afficher Tous les Memos\n\nShow_Private_Appointments = Afficher Rendez-vous Priv\\u00E9s|V\n\nShow_Public_Appointments = Afficher Rendez-vous Publics|P\n\nShow_Stack_Trace = Afficher Trace Pile\n\nShow_U.S._Holidays = Show U.S. Holidays|U\n\nSt._Patrick's_Day = St. Patrick's Day\n\nStandard_Time = Standard Time\n\nStartDate = Date de d\\u00E9but\n\nStart_Date = Date de D\\u00E9but|D\n\nStart_Time\\: = D\\u00E9but:|A\n\nState_Change = Changer Etat\n\nStates = Etats\n\nStatus = Status|U\n\nSubTasks = Sous-t\\u00E2ches\n\nTaskInformation = Information T\\u00E2che\n\nTask_Created = T\\u00E2che cr\\u00E9\\u00E9e\n\nTask_State_Editor = Editeur Etat T\\u00E2che\n\nTask_Types = Types T\\u00E2ches\n\nText = Texte\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = Time\n\nTimes = Heures|M\n\nTo_Do = ToDo|T|ctrl T\n\nTo_Do_List = Liste ToDo|D\n\nToday = Aujourd'hui|T|HOME\n\nType = Type|Y\n\nUse_24_hour_time_format = Utiliser le format 24 heures|2\n\nUse_system_beep = Utiliser le Beep System\n\nUserColorScheme = User Color Scheme\n\nVacation = Cong\\u00E9|C\n\nValentine's_Day = Valentine's Day\n\nVeteran's_Day = Veteran's Day\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = Page Web:|G\n\nWeek_Starts_with_Monday = Lundi D\\u00E9but de Semaine|M\n\nWeek_View = Vue Hebdomadaire\n\nWeek_View_End_Hour\\:_ = Heure Fin Journ\\u00E9e:|E\n\nWeek_View_Start_Hour\\:_ = Heure D\\u00E9but Journ\\u00E9e:|S\n\nWorkAddress = Adresse Professionnelle\n\nWork_City\\: = Ville:|I\n\nWork_Country\\: = Pays:|R\n\nWork_Phone = T\\u00E9l\\u00E9phone Professionnel\n\nWork_Phone\\: = T\\u00E9l\\u00E9phone Professionnel:|W\n\nWork_State\\: = Etat:|E\n\nWork_Street_Address = Rue|D\n\nWork_Zip_Code\\: = Code:|O\n\nYear_View = Vue Annuelle\n\nYour_Email_Address = Votre Adresse Email|Y\n\n]_does_not_exist = ] n'existe pas\n\n]_is_not_a_directory = ] n'est pas un r\\u00E9pertoire\n\n__through__ = \\u00E0\n\naddcat = Ajouter Nouvelle Cat\\u00E9gorie|A\n\naddresses = Adresses\n\nall_undos = Afficher la pile Annuler (lecture seule)\n\nappearance = Apparence\n\napply = Appliquer|A\n\nappointment = Rendez-vous\n\nappointments = Rendez-vous\n\nappt_error = La fen\\u00EAtre rendez-vous va se fermer \\u00E0 cause d'une erreur pr\\u00E9c\\u00E9dente. Peut-\\u00EAtre un rendez-vous a \\u00E9t\\u00E9 supprim\\u00E9 de la fen\\u00EAtre ToDo alors que l'\\u00E9diteur \\u00E9tait ouvert. Sinon, red\\u00E9marrer Borg\n\napptlist = Liste Rendez-vous\n\nappttext = Texte Rendez-vous\n\nappttime = Heure Rendez-vous\n\natt_folder_err = Impossible de cr\\u00E9er un r\\u00E9pertoire pour attachement:\n\natt_not_found = Impossible de trouver un objet li\\u00E9\n\natt_owner_null = Impossible de sauvegarder le lien avant que ce nouvel item ne soit sauvegarder dans la base de donn\\u00E9es\n\nattach_file = Attacher Fichier\n\nbackup_dir = R\\u00E9pertoire Backup\n\nbackup_notice = Ecrire donn\\u00E9es backup dans\n\nbad_db_2 = \\\\nSi un mauvais r\\u00E9pertoire de base de donn\\u00E9es provoque l'erreur, svp click YES pour en changer.\n\nbiweekly = bi-hebdommadaire\n\nblack = noir\n\nblue = bleu\n\ncalShowSubtask = Afficher Sous-t\\u00E2ches dans Calendrier/Todo\n\ncalShowTask = Afficher T\\u00E2ches dans Calendrier/Todo\n\ncase_sensitive = Sensible \\u00E0 la Casse\n\ncat_choose = Choisir Cat\\u00E9gorie\n\ncatchooser = S\\u00E9lectionneur Cat\\u00E9gories\n\nchangedate = Changer Date|H\n\nchg_cat = Changer Cat\\u00E9gorie\n\nchoose_file = Choisir Fichier\n\nchoosecat = Choisir Cat\\u00E9gories \\u00E0 Afficher|C\n\nclear_all = Tout Effacer|C\n\nclear_undos = Effacer Log Annuler\n\nclose_date = Date Fermeture\n\nclose_proj_warn = Impossible de fermer le project. Au moins tant qu'une t\\u00E2che fille n'est pas ferm\\u00E9e.\n\nclose_tabs = Fermer tous les onglets\n\ncollapse = R\\u00E9duire\n\nconfirm_overwrite = Confirmer Ecrasement\n\ncontact = Information Contact\n\ncontributions_by = Remerciements pour leurs Contributions \\u00E0:\n\ncopy_appt = Copier Rendez-vous\n\ncopyright = Copyright\n\ncreated = Cr\\u00E9\\u00E9\n\ncustom_times_header = Heures Popup Rappel pour\n\ndaily = journalier\n\ndb_set_to = \\\\n\\\\nVotre r\\u00E9pertoire db est configur\\u00E9 \\u00E0:\n\ndel_tip = Supprimer toutes les occurrences (r\\u00E9p\\u00E9titions) d'un rendez-vous\n\ndelcat_warn = WARNING! TOUS les rendez-vous et t\\u00E2ches sont en cours de suppression pour la cat\\u00E9gorie\n\ndelete_cat = Supprimer Cat\\u00E9gorie|D\n\ndelete_cat_choose = Choisir Cat\\u00E9gorie \\u00E0 Supprimer\n\ndelete_selected = Supprimer S\\u00E9lectionn\\u00E9\n\ndeleted = Supprim\\u00E9\n\ndlist = S\\u00E9lectionner Jours\n\ndock = Dock\n\ndone = fait\n\ndoo_tip = Supprimer seulement l'occurrence en cours d'un rendez-vous qui se r\\u00E9p\\u00E8te\n\nduration = Dur\\u00E9e\n\ndview_font = Police Vue Journali\\u00E8re\n\nedit_types = Editer Types et Etats de T\\u00E2che|E\n\nelapsed_time = Ecoul\\u00E9\n\nempty_desc = Svp entrer une Description\n\nenable_popups = Activer Rappels Popup\\n|P\n\nenable_systray = Activer System Tray Icon (n\\u00E9cessite de red\\u00E9marrer)\n\nenturl = Entrer URL:\n\nep = Editer Pr\\u00E9f\\u00E9rences|E\n\nexpXML = Exporter XML...|E\n\nexpand = D\\u00E9velopper\n\nexport = Exporter\n\nexport_prefs = Exporter Pr\\u00E9f\\u00E9rences\n\nfonts = Polices\n\nforever = R\\u00E9p\\u00E9ter ind\\u00E9finiment |F\n\ngreen = vers\n\nh2info = Information H2\n\nhaslinks = A des Liens\n\nhide_strike = Masquer Items barr\\u00E9s\n\nhistory = Historique\n\nhsqldb = HSQLDB\n\nhsqldbinfo = Information HSQLDB\n\nimpXML = Importer XML...|I\n\nimpexpMenu = Import / Export|I\n\nimport_prefs = Importer Pr\\u00E9f\\u00E9rences\n\njdbc = JDBC g\\u00E9n\\u00E9rique\n\nlink_file = Fichier de liens\n\nlinks = Liens\n\nlocale = Locale (n\\u00E9cessite de red\\u00E9marrer le programme)|O\n\nmemo = Memo\n\nmin_aft_app = minutes apr\\u00E8s le rendez-vous\n\nmin_bef_app = minutes avant le rendez-vous\n\nminute_reminder = rappel minute\n\nminutes_ago = Il y a plusieurs minutes!\n\nmisc = Divers\n\nmonthly = mensuel (date)\n\nmonthly_day = mensuel (day)\n\nmview_font = Police d'impression\n\nmwf = Lun-Mer-Ven\n\nnavy = navy\n\nndays = Chaque N Jours\n\nnewDate\\: = Nouvelle Date:|N\n\nnoOutput = Commande SQL ne renvoie rien ou des erreurs\n\nno_undos = Rien \\u00E0 annuler\n\nnummonths = Nombre de Mois \\u00E0 imprimer?\n\nonce = une fois\n\nopen_subtasks = Impossible de Fermer T\\u00E2che, les Sous-t\\u00E2ches ne sont pas toutes ferm\\u00E9es\n\nopen_tasks = T\\u00E2ches Ouvertes\n\noverwrite_warning = Fichier Existe. OK pour Ecraser Fichier:\n\nparent = Parent\n\nplease_confirm = Svp Confirmer\n\npopup_reminders = Rappels Popup\n\nprojchild_warning = Impossible de sauvegarder le projet. Un projet fils a une \\u00E9ch\\u00E9ance post\\u00E9rieure \\u00E0 celle du projet\n\nprojdd_warning = Impossible de sauvegarder le projet. Une t\\u00E2che fille a une \\u00E9ch\\u00E9ance post\\u00E9rieure \\u00E0 celle du projet\n\nproject = Projet\n\nproject_tree = Arborescence Projet\n\nprojects = Projets\n\nprojpar_warning = Impossible de sauvegarder le projet. Son \\u00E9ch\\u00E9ance est post\\u00E9rieure \\u00E0 celle de son projet p\\u00E8re\n\nrecur_compat = Error: R\\u00E9currence n'est pas compatible avec la date de rendez-vous\n\nred = rouge\n\nremcat = Supprimer Cat\\u00E9gories inutilis\\u00E9es|R\n\nreminder_time = Rappel Email Heure|R\n\nrepeating = En cours de r\\u00E9p\\u00E9tition\n\nreset_state_warning = Ceci re-initialisera toutes les modifications effecu\\u00E9es sur le mod\\u00E8le t\\u00E2che\\\\nLe mod\\u00E8le t\\u00E2che sera re-initialis\\u00E9 \\u00E0 son \\u00E9tat par d\\u00E9faut.\\\\nVoulez-vous vraiment le faire?\\\\n\n\nrestore_defaults = r\\u00E9tablir les d\\u00E9fauts\n\nrlsnotes = Release Notes\n\nsave_Def = Sauvegarder D\\u00E9fauts\n\nsd_dd_warn = Error: Ech\\u00E9ance ne peut pas \\u00EAtre avant la Date de D\\u00E9but\n\nsd_tip = Sauvegarder les valeurs en cours comme les valeurs par d\\u00E9faut pour Rendez-vous\n\nselect_all = Tout S\\u00E9lectionner|S\n\nselect_appt = Svp s\\u00E9lectionner un rendez-vous\n\nselect_export_dir = S\\u00E9lectionner R\\u00E9pertoire Export\n\nselect_link_type = S\\u00E9lectionner Type Objet pour Lien\n\nselectdb = La Base de donn\\u00E9es n'est pas configur\\u00E9e. Svp entrer la configuration de la base de donn\\u00E9es.\n\nsend_reminder = Envoyer Email Rappel|E\n\nset_def_font = Police par D\\u00E9faut|D\n\nshow_closed = Afficher Projets Termin\\u00E9s\n\nshow_rpt_num = Afficher Nombre de R\\u00E9p\\u00E9titions|W\n\nshowdoy = Afficher Jour de L'ann\\u00E9e|Y\n\nshutdown = Arr\\u00EAt en cours....Attendre svp\n\nsocket_port = Socket Port (-1=disactiv\\u00E9, n\\u00E9cessite un red\\u00E9marrage)\n\nsocket_warn = Socket Port doit \\u00EAtre un entier\n\nsplash = Afficher \\u00E9cran splash|S\n\nsrch = Rechercher|S|F3\n\nstackonerr = Afficher Stack Trace Option sur Dialogues Erreur|K\n\nstdd_warning = Impossible de sauvegarder la t\\u00E2che. Une sous-t\\u00E2che a une \\u00E9ch\\u00E9ance post\\u00E9rieure \\u00E0 celle de la t\\u00E2che\n\nstrike = barr\\u00E9\n\nstripecolor = Table Trait de Couleur\n\nstsd_warning = Impossible de sauvegarder la t\\u00E2che. Une sous-t\\u00E2che a une date de d\\u00E9but ant\\u00E9rieure \\u00E0 celle de la t\\u00E2che\n\nsubject = Sujet\n\nsubtask = Sous-t\\u00E2che\n\nsubtask_id = Sous-t\\u00E2che ID\n\ntask = T\\u00E2che\n\ntaskOptions = Options T\\u00E2che\n\ntask_abbrev = Afficher Num\\u00E9ros des T\\u00E2ches sur le Calendrier\n\ntaskdd_warning = Impossible de sauvegarder la t\\u00E2che avec une \\u00E9ch\\u00E9ance post\\u00E9rieure \\u00E0 celle du projet\n\ntasks = T\\u00E2ches\n\ntodomissingdata = Svp entrer texte et date du todo.\n\ntodoquickentry = Rapide Todo\n\ntotal_tasks = Total des t\\u00E2ches\n\ntruncate_appts = Couper Rendez-Vous dans Vue Mensuelle|T\n\ntth = Mar-Jeu\n\nucolortext1 = utiliser les couleurs utilisateur dans la liste todo|T\n\nucolortext10 = vacances\n\nucolortext11 = anniversaires\n\nucolortext12 = d\\u00E9faut\n\nucolortext13 = vacance\n\nucolortext14 = demi-journ\\u00E9e\n\nucolortext15 = vacance\n\nucolortext16 = aujourd'hui\n\nucolortext17 = weekend\n\nucolortext18 = jour semaine\n\nucolortext2 = cocher todo dans vue mensuelle|M\n\nucolortext4 = couleur texte 1\n\nucolortext5 = couleur texte 2\n\nucolortext6 = couleur texte 3\n\nucolortext7 = couleur texte 4\n\nucolortext8 = couleur texte 5\n\nucolortext9 = t\\u00E2ches\n\nuncategorized = <Aucune Cat\\u00E9gorie>\n\nundo = Annuler\n\nunknown = inconnu\n\nupdated = Mis \\u00E0 Jour\n\nurl = URL\n\nuser = utilisateur\n\nviewchglog = Change Log|V\n\nweekdays = Lundi-Vendredi\n\nweekends = Samedi et Dimanche\n\nweekly = hebdomadaire\n\nwhite = blanc\n\nwview_font = Police Vue Hebdomadaire\n\nyearly = annuel\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_it.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NUOVO APPUNT *****\n\n-db_argument_is_missing = -argomento mancante del db\n\nAbout = Informazioni su Borg\n\nAbout_BORG = About BORG\n\nAction = Azione\n\nAdd = Aggiungi\n\nAddCat = Inserisci Nuova Categoria:\n\nAdd_New = Aggiungi Nuovo\n\nAdd_State = Aggiungi Stato\n\nAdd_Type = Aggiungi Tipo\n\nAddress = Indirizzo\n\nAddress_Book = Agenda\n\nAddress_Book_Entry = Inserimento in Agenda\n\nAll = Tutte\n\nApply_DB_Change = Applica Modifica al DB\n\nAppointment_Editor = Modifica Appuntamento\n\nAppointment_Editor_for_ = Modifica Appuntamento per \n\nBerger-Organizer_v = Berger-Organizer versione \n\nBirthday = Compleanno\n\nBoxing_Day = Boxing Day\n\nBrowse = Esplora\n\nBuild_Time\\:_ = \\\\nData Prodotto: \n\nCLOSED = CHIUSO\n\nCanada_Day = Canada Day\n\nCancel = Annulla\n\nCategories = Categorie\n\nCategory = Categoria\n\nCell_Phone\\: = Cellulare:\n\nChange = Modifica\n\nCheckLists = Spunta\n\nChristmas = Natale\n\nCivic_Holiday = Vacanze\n\nClone = Clona\n\nClose = Chiudi\n\nClosed = Chiuse\n\nColor = Colore\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = Azienda\n\nConfirm_DB_Change = Conferma Modifica DB\n\nConfirm_Delete = Conferma Cancellazione\n\nCopy = Copia\n\nCould_not_parse_times\\:_ = Non posso analizzare le ripetizioni: \n\nDataBase_Directory = Cartella del DataBase\n\nDatabaseInformation = Informazione Database\n\nDate = Data\n\nDay_View = Vista Giorno\n\nDaylight_Savings_Time = Ora Legale\n\nDays = Giorni\n\nDays_Left = Giorni Rimanenti\n\nDelete = Cancella\n\nDelete_One_Only = Cancella Solo Uno\n\nDelete_State = Cancella Stato\n\nDelete_Type = Cancella Tipo\n\nDescription = Descrizione\n\nDirectory_[ = Directory [\n\nDiscard_Text? = Scarica Testo?\n\nDismiss = Chiudi\n\nDone_(Delete) = Fatto (Cancella)\n\nDone_(No_Delete) = Fatto (Non Cancellare)\n\nDueDate = Data Fine\n\nDue_Date = Data Fine\n\nEdit = Modifica\n\nEditNew = Modifica Nuovo\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = Parametri Email\n\nEnable_Email = Abilita Email\n\nEncryptOnSave = Cripta e Salva\n\nEncryption = Crittazione\n\nExit = Esci\n\nFather's_Day = Festa del Pap\\u00E0\n\nFax\\: = Fax:\n\nFilter\\: = Filtro:\n\nFirst = Nome\n\nFirst_Name\\: = Nome:\n\nFrequency = Frequenza\n\nGo_To = Vai a\n\nGround_Hog_Day = Giorno della Marmotta\n\nHalf_Day = Mezza Giornata\n\nHalloween = Halloween\n\nHelp = Aiuto\n\nHoliday = Festivo\n\nHomeAddress = Indirizzo Casa\n\nHome_City\\: = Citt\\u00E0:\n\nHome_Country\\: = Paese:\n\nHome_Phone = Tel. Casa\n\nHome_Phone\\: = Tel. Casa:\n\nHome_State\\: = Provincia:\n\nHome_Street_Address = Indirizzo\n\nHome_Zip_Code\\: = CAP:\n\nISO_week_number = Usa ISO 8601 in Numerazione Settimanale\n\nImport = Importa\n\nImport_WARNING = Avvertimento Importazione\n\nImporting_ = Importazione\n\nIndependence_Day = Giorno d'Indipendenza\n\nInitializing = Inizializzazione\n\nItem = Voce\n\nItem_ = Voce\n\nItem_\\# = Voce nr.\n\nKeyStore = Chiave\n\nLabor_Day = Festa dei Lavoratori\n\nLabour_Day_(Can) = Festa dei Lavoratori (Can)\n\nLast = Cognome\n\nLast_Name\\: = Cognome:\n\nLicense = Licenza\n\nLook_and_Feel\\: = Aspetto grafico:\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemorial_Day = Giorno della Memoria\n\nMemos = Memo\n\nMinutes = Minuti\n\nMonth_View = Mostra Mese\n\nMother's_Day = Festa della Mamma\n\nNEW_Item = Nuova voce\n\nNew_State = Nuovo Stato\n\nNew_Task_Type = Nuovo Tipo Attivit\\u00E0\n\nNew_Year's_Day = Capodanno\n\nNext_States = Prossimo Stato\n\nNickname\\: = Nickname:\n\nNo_Specific_Time = Ora non specificata\n\nNotes = Note\n\nNotice = Nota\n\nOK = OK\n\nOpen = Aperte\n\nOpen_Calendar = Apri Calendario\n\nOptions = Opzioni\n\nPA = n. Pers.\n\nPager\\: = Cerca:\n\nPassword = Password\n\nPlease_choose_File_to_Import_From = Scegli il File da Importare\n\nPlease_choose_directory_to_place_XML_files = Scegli la directory in cui esportare il File XML\n\nPlease_enter_some_appointment_text = Inserisci del testo sull'appuntamento\n\nPlease_select_a_state = Seleziona uno stato\n\nPlease_select_a_type = Seleziona un tipo\n\nPopup_Times = Tempo per i Popup\n\nPresidents_Day = Giorno dei Presidenti\n\nPri = Priorit\\u00E0\n\nPrint = Stampa\n\nPrint_Chooser = Scegli Stampante\n\nPrint_In_Color? = Stampa a Colori?\n\nPriority = Priorit\\u00E0\n\nPrivate = Privato\n\nProperties = Propriet\\u00E0\n\nReally_Delete_ = Cancella Veramente \n\nReally_change_the_database? = Modificare Veramente il database?\n\nReally_delete_number_ = Eliminare veramente il numero \n\nRecurrence = Ricorrenza\n\nRemembrance_Day = Remembrance Day\n\nRename_State = Rinomina Stato\n\nRename_Type = Rinomina Tipo\n\nReset_Task_States_to_Default = Resetta Stato Attivit\\u00E0 al Default\n\nResolution = Soluzione\n\nRunSQL = Esegui SQL\n\nSMTP_Server = SMTP Server\n\nSave = Salva\n\nSave_&_Close = Salva e Chiudi|C|\n\nScreen_Name\\: = Nome visualizzato:\n\nSearch_Results = Risultato Ricerca\n\nSelect = Scegli\n\nSelect_next_state = Seleziona prossimo stato\n\nShow_Canadian_Holidays = Mostra Vacanze Canadesi\n\nShow_Private_Appointments = Mostra Appuntamenti Privati\n\nShow_Public_Appointments = Mostra Appuntamenti Pubblici\n\nShow_Stack_Trace = Mostra Traccia della Lista\n\nShow_U.S._Holidays = Mostra vacanze U.S.A.\n\nSt._Patrick's_Day = Festa di S. Patrizio\n\nStandard_Time = Orario Ufficiale\n\nStartDate = Data Inizio\n\nStartToSysTray = Inizia nella System Tray\n\nStart_Date = Data Inizio\n\nStart_Time\\: = Ora Inizio:\n\nStates = Stati\n\nStatus = Stato\n\nSubTasks = Sotto-Attivit\\u00E0\n\nTaskInformation = Informazione Attivit\\u00E0\n\nTask_State_Editor = Editor Stato Attivit\\u00E0\n\nTask_Types = Tipi di Attivit\\u00E0\n\nText = Testo\n\nThanksgiving = Festa del Ringraziamento\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = Ora\n\nTimes = Volte\n\nTo_Do = Da-Fare\n\nTo_Do_List = Lista Da-Fare\n\nToday = Oggi\n\nType = Tipo\n\nUse_24_hour_time_format = Usa formato di 24 ore\n\nUserColorScheme = Colori Utente\n\nVacation = Vacanza\n\nValentine's_Day = San Valentino\n\nVeteran's_Day = Festa dei Veterani\n\nVictoria_Day = Giorno della Vittoria\n\nWeb_Page\\: = Pagina Web:\n\nWeek_Starts_with_Monday = Settimana inizia dal Luned\\u00EC\n\nWeek_View = Vista Settimana\n\nWeek_View_End_Hour\\:_ = Settimana: ora finale \n\nWeek_View_Start_Hour\\:_ = Settimana: ora iniziale \n\nWorkAddress = Indirizzo Lavoro\n\nWork_City\\: = Citt\\u00E0:\n\nWork_Country\\: = Paese:\n\nWork_Phone = Tel. Lavoro\n\nWork_Phone\\: = Tel. Lavoro:\n\nWork_State\\: = Provincia:\n\nWork_Street_Address = Indirizzo\n\nWork_Zip_Code\\: = CAP:\n\nYear_View = Vista Anno\n\nYour_Email_Address = Tuo indirizzo Email\n\n]_does_not_exist = ] non esiste\n\n]_is_not_a_directory = ] non \\u00E8 una directory\n\n__through__ = fino a  \n\naddcat = Aggiungi Nuova Categoria\n\nappearance = Aspetto\n\napply = Applica\n\nappt_error = La finestra degli appuntamenti verr\\u00E0 chiusa a causa di un errore precedente. Forse un appuntamento \\u00E8 stato rimosso dalla finestra Da-Fare mentre l'editor era ancora aperto. Altrimenti, riavvia Borg\n\napptlist = Lista Appuntamenti\n\nappttext = Testo Appuntamento\n\nappttime = Ora Appuntamento\n\nattach_file = File Allegato\n\nbackup_dir = Cartella dei Backup\n\nbad_db_2 = \\\\nSe la directory del DB causa un errore, clicca su YES per cambiarla.\n\nbeep_options = Suono per pro-Memoria\n\nbiweekly = bimensile\n\nblack = nero\n\nblue = blue\n\ncalShowSubtask = Mostra le Sottoattivit\\u00E0 nel Calendario/Da-fare\n\ncalShowTask = Mostra le Attivit\\u00E0 nel Calendario/Da-fare\n\ncatchooser = Scelta Categoria\n\nchangedate = Cambia Data\n\nchoose_file = Scegli File\n\nchoosecat = Scegli Categorie da Visualizzare\n\nclear_all = Cancella tutto\n\nclose_tabs = Chiudi tutti i Tabs\n\ncontact = Informazione Contatto\n\ncopy_appt = Copia Appuntamento\n\ndaily = giornaliero\n\ndb_set_to = \\\\n\\\\nDirectory DataBase impostata: \n\ndel_tip = Cancella tutte le occorrenze (ripetizioni) di un appuntamento\n\ndelete_cat = Cancella Categoria|D\n\ndelete_cat_choose = Scegli Categoria da cancellare\n\ndelete_selected = Cancella quello che hai Selezionato\n\ndoo_tip = Cancella solo questa occorrenza dell'appuntamento ripetuto\n\nedit_types = Modifica Tipi e Stati di Attivit\\u00E0\n\nenable_popups = Abilita Popup per ProMemoria\n\nenable_systray = Abilita Icona nella System Tray (richiede restart)\n\nenable_tls = Abilita TLS\n\nenturl = Inserisci URL:\n\nep = Preferenze\n\nexpXML = Esporta XML...\n\nexport_prefs = Esporta Preferenze\n\nforever = Ripeti Sempre\n\ngradient_appts = Usa Colore sfumato nel box Appuntamento\n\ngreen = verde\n\nh2info = Informazioni su H2\n\nhide_strike = Nascondi le voci annullate\n\nhsqldbinfo = Informazioni su HSQLDB\n\nimpXML = Importa XML...\n\nimpexpMenu = Importa / Esporta\n\nimport_prefs = Importa Preferenze \n\nimport_zip = Importa tutto il file compresso di Backup\n\njdbc = JDBC Generico\n\nlink_file = Link file\n\nlocale = Locale (richiede restart)|O\n\nmin_aft_app = minuti dopo l'appuntamento\n\nmin_bef_app = minuti prima dell'appuntamento\n\nmisc = Varie\n\nmonthly = mensile (data)\n\nmonthly_day = mensile (giorno)\n\nmwf = Lun-Mer-Ven\n\nnavy = marine\n\nndays = Ogni N Giorni\n\nnewDate\\: = Nuova Data:\n\nno_undos = Nessun Undo\n\nnummonths = Numero di Mesi da stampare?\n\nonce = una volta\n\nopen_subtasks = Non si puo chiudere la Attivit\\u00E0. Le SottoAttivit Attivit\\u00E0 non sono tutte chiuse.\n\nopen_tasks = Attivit\\u00E0 aperte\n\npopup_reminders = Popup per ProMemoria\n\nproject_tree = Albero Progetti\n\nprojects = Progetti\n\npw_time = Scadenza Password (in secondi):\n\nred = rosso\n\nremcat = Rimuovi Categoria Inutilizzata\n\nreminder_list = Mostra tutti i ProMemoria in una sola finestra (richiede restart)\n\nreset_state_warning = Questo resetter\\u00E0 ogni modifica fatta prima, riportando allo stato di default\\\\nVuoi veramente questo?\\\\n\n\nrestore_defaults = ripristina i defaults\n\nrlsnotes = Note di rilascio\n\nsave_Def = Salva Default\n\nsd_tip = Salva valori attuali come nuovi Appuntamenti predefiniti\n\nselect_all = Seleziona tutto\n\nselect_export_dir = Seleziona la cartella su cui esportare\n\nselectdb = Database non impostato. Impostare DB.\n\nsend_reminder = Email pro-Memoria|E\n\nset_def_font = Imposta Font di Default\n\nshow_date_in_systray = Mostra Data nella System Tray\n\nshow_task_status_in_tree = Mostra Numero e Stato Attivit\\u00E0 nel Albero delle Attivit\\u00E0\n\nshowdoy = Mostra Giorno dell'anno\n\nshutdown = In chiusura....attendere\n\nsocket_port = Porta Socket (-1=disabilitata, richiede restart)\n\nsort_by_priority = Ordina Appuntamenti per Priorit\\u00E0\n\nsplash = Mostra schermata di Borg\n\nsrch = Ricerca\n\nstackonerr = Mostra Opzioni Attivit\\u00E0 nel Dialogo Errore\n\nstrike = linea-cancellazione\n\nstripecolor = Tavola colori a strisce\n\nsubject = Oggetto\n\ntaskOptions = Opzioni Attivit\\u00E0\n\ntask_abbrev = Mostra solo il Numero nel Calendario\n\ntasks = Attivit\\u00E0\n\ntodo_option_auto_clear_text = Pulisci il testo del Da-Fare quando aggiungi\n\ntodo_option_auto_date_today = La data di default \\u00E8 Oggi\n\ntodomissingdata = Inserisci testo e data del Da-Fare.\n\ntodoquickentry = Inserimento Rapido Da-Fare *\n\ntotal_tasks = Totale Attivit\\u00E0\n\ntruncate_appts = Tronca Appuntamenti nella Vista Mese|T\n\ntth = Mar-Gio\n\nucolortext1 = Colore utente in vista Da-Fare|T\n\nucolortext10 = festivit\\u00E0\n\nucolortext11 = Compleanni\n\nucolortext12 = default\n\nucolortext13 = festa\n\nucolortext14 = mezzagiornata\n\nucolortext15 = vacanze\n\nucolortext16 = oggi\n\nucolortext17 = fine settimana\n\nucolortext18 = giorno della settimana\n\nucolortext2 = Marca da-fare in vista mese|M\n\nucolortext4 = colore testo 1\n\nucolortext5 = colore testo 2\n\nucolortext6 = colore testo 3\n\nucolortext7 = colore testo 4\n\nucolortext8 = colore testo 5\n\nucolortext9 = attivit\\u00E0\n\nuncategorized = <Nessuna Categoria>\n\nunknown = sconosciuto\n\nviewchglog = CHANGELOG del programma\n\nweekdays = Luned\\u00EC-Venerd\\u00EC\n\nweekends = Sabato e Domenica\n\nweekly = settimanale\n\nwhite = bianco\n\nyearly = annuale\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_nl.properties",
    "content": "\n*****_NEW_APPT_***** = Nieuwe Afspraak\n\n-db_argument_is_missing = -Datenbank-Wert fehlt\n\nAbout = Over...\n\nAbout_BORG = Over BORG\n\nAction = Aktie\n\nAdd = Toevoegen\n\nAddCat = Nieuwe Categorie maken:\n\nAdd_New = Nieuwe invoer\n\nAddress = Adres\n\nAddress_Book = Adressenboek\n\nAddress_Book_Entry = Invoer Adressenboek\n\nAll = Alles\n\nApply_DB_Change = DB-verandering gebruiken\n\nAppointment_Editor = Afspraak-Editor\n\nAppointment_Editor_for_ = Afspraak planner voor \n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Geboortedatum\n\nBrowse = Zoeken\n\nBuild_Time\\:_ = \\\\Creeer Tijd:\\\\n\n\nCLOSED = BEEINDIGD\n\nCategories = Categorie\n\nCategory = Kategorie\n\nCell_Phone\\: = Mobiele Telefoon:\n\nChange = Wijzigen\n\nChristmas = Kerstmis\n\nClone = Copieer\n\nClose = Sluiten\n\nClosed = Einde\n\nColor = Kleur\n\nCommonwealth_Day = Commonwealth Dag\n\nCompany = Bedrijf\n\nConfirm_DB_Change = Database-wijziging bevestigen.\n\nCould_not_parse_times\\:_ = Kon Tijden niet verwerken: \n\nDataBase_Directory = DB-Directory\n\nDatabaseInformation = Database-Informationen\n\nDate = Datum\n\nDaylight_Savings_Time = rest Dagtijd\n\nDays = Dagen\n\nDays_Left = Overige Dagen\n\nDelete = Verwijderen\n\nDelete_One_Only = verwijderen eenmalig\n\nDescription = Beschrijving\n\nDirectory_[ = Map [\n\nDiscard_Text? = Tekst verwijderen?\n\nDismiss = Sluiten\n\nDone_(Delete) = gedaan (wissen)\n\nDone_(No_Delete) = gedaan (niet wissn)\n\nDueDate = Einddatum\n\nDue_Date = Einddatum\n\nEdit = Wijzigen\n\nEditNew = Nieuwe bewerking\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = Email-Instellingen\n\nEnable_Email = Gebruik Email\n\nExit = Afsluiten\n\nFax\\: = Fax:\n\nFilter\\: = Filter:\n\nFirst = Voornaam\n\nFirst_Name\\: = Voornaam:\n\nFrequency = Frequentie\n\nGo_To = Ga naar...\n\nHalf_Day = Halve Dag\n\nHelp = Help\n\nHoliday = Vrije dag\n\nHomeAddress = Adres thuis\n\nHome_City\\: = Plaats:\n\nHome_Country\\: = Staat:\n\nHome_Phone = Telefoon thuis\n\nHome_Phone\\: = Telefoon thuis:\n\nHome_State\\: = Land:\n\nHome_Street_Address = Straat\n\nHome_Zip_Code\\: = Postcode:\n\nImport = Import\n\nImport_WARNING = Waarschuwing\n\nImporting_ = Importeer\n\nInitializing = Initialiseren\n\nItem = Invoer\n\nItem_ = Invoer \n\nItem_\\# = Invoer #\n\nLast = Achternaam\n\nLast_Name\\: = Achternaam:\n\nLicense = Licentie\n\nLook_and_Feel\\: = Voorbeeld:\n\nMonth_View = Maand overzicht\n\nNEW_Item = Nieuwe invoer\n\nNew_Year's_Day = Nieuwjaar\n\nNickname\\: = Alias:\n\nNo_Specific_Time = Geen vastgestelde tijd\n\nNotes = Opmerkingen\n\nNotice = Bericht\n\nOK = OK\n\nOpen = Open\n\nOpen_Calendar = Openen Kalender\n\nOptions = Opties\n\nPA = Persoon\n\nPager\\: = Mobiel:\n\nPassword = Paswoord\n\nPlease_choose_File_to_Import_From = Geef import bestand aan\n\nPlease_choose_directory_to_place_XML_files = Geef een verwijzing voor de  XML-Data\n\nPlease_enter_some_appointment_text = AUB Omschrijving Invoeren\n\nPri = Prioriteit\n\nPrint = Afdrukken\n\nPrint_Chooser = Printer-Keuze\n\nPrint_In_Color? = Kleur afdrukken?\n\nPrivate = Prive\n\nProperties = Eigenschappen\n\nReally_change_the_database? = Database wijzigen \\u00E4ndern?\n\nReally_delete_number_ = Verwijder getal \n\nReset_Task_States_to_Default = Plaats Opdrachten terug\n\nResolution = Resolutie\n\nSMTP_Server = SMTP Server\n\nSave = Opslaan\n\nScreen_Name\\: = Ingevoerde Naam:\n\nSearch_Results = Zoek resultaat\n\nSelect = Selecteren\n\nShow_Canadian_Holidays = Canadeese vrijedagen\n\nShow_Private_Appointments = Toon prive Afspraken\n\nShow_Public_Appointments = Toon algemene Afspraken\n\nShow_Stack_Trace = Toon Stack-Trace\n\nStartDate = Startdatum\n\nStart_Date = Startdatum\n\nStart_Time\\: = Aanvangstijd:\n\nStatus = Status\n\nTaskInformation = Opdracht-Informatie\n\nText = Tekst\n\nTime = Tijd\n\nTimes = Aantal\n\nTo_Do = Te Doen\n\nTo_Do_List = Te Doen overzicht\n\nToday = Vandaag\n\nType = Type\n\nUse_24_hour_time_format = 24-Uur-Datum\n\nVacation = Vakantie\n\nWeb_Page\\: = Webpagina:\n\nWeek_Starts_with_Monday = Week start met Maandag\n\nWeek_View = Weekoverzicht\n\nWeek_View_End_Hour\\:_ = Day eindigd om: \n\nWeek_View_Start_Hour\\:_ = Day start om: \n\nWorkAddress = Adres Werk\n\nWork_City\\: = Plaats:\n\nWork_Country\\: = Staat:\n\nWork_Phone = Telefoon werk\n\nWork_Phone\\: = Telefon Werk:\n\nWork_State\\: = Land:\n\nWork_Street_Address = Straat\n\nWork_Zip_Code\\: = Postcode:\n\nYour_Email_Address = Uw Email-Adres\n\n]_does_not_exist = ] niet gevonden\n\n]_is_not_a_directory = ] is geen Map\n\n__through__ = tot\n\naddcat = Nieuwe Categorie\n\nappearance = Voorbeeld\n\napply = Vervang\n\nappt_error = Het afspraken venster wordt door een eerdere fout gesloten.||Mogelijk werd een Afspraak uit het Te Doen venster gesloten terwijl de Editor open stond. Zo niet, BORG opnieuw starten.\n\napptlist = Afsprakenlijst\n\nappttext = Afspraaktekst\n\nappttime = Tijdstip afspraak\n\nbad_db_2 = \\\\nAls een foutieve Database verwijzingde fout veroorzaakt,\\\\n AUB 'JA' kiezen, om verandering aan te brengen.\n\nbiweekly = twee-weekelijks\n\nblack = Zwart\n\nblue = Blauw\n\ncatchooser = Categorie-Keuze\n\nchangedate = Andere Datum\n\nchoose_file = Kies Bestand\n\nchoosecat = Categorie Kiezen\n\ncontact = Kontakt-Informatie\n\ndaily = dagelijks\n\ndb_set_to = \\\\n\\\\nJe Database-Verwijzing is nu: \n\ndel_tip = Verwijder alle voorkomende herhalingen van een Afspraak\n\ndoo_tip = Verwijder enkel deze voorkomende en herhalende Afspraken\n\nenable_popups = Popup-reminder aktiveren\\n\n\nenturl = URL invoeren:\n\nep = Instellingen\n\nexpXML = Exporteer XML...\n\nforever = Eindeloos herhalen\n\ngreen = Groen\n\nimpXML = Importeer XML...\n\nimpexpMenu = Import / Export\n\nlocale = Land (herstarten aanbevolen)\n\nmin_aft_app = Minuten na Afspraak\n\nmin_bef_app = Minuten voor Afspraak\n\nmisc = Overige\n\nmonthly = maandelijks (Datum)\n\nmonthly_day = maandelijks (Tag)\n\nmwf = Maan-Woens-Vrij\n\nnavy = Donker Blauw\n\nnewDate\\: = Nieuwe Datum:\n\nnummonths = Hoeveel Maanden Afdrukken?\n\nonce = eenmaal\n\npopup_reminders = Popup-reminder\n\nred = Rood\n\nremcat = Verwijder Categorie\n\nreset_state_warning = Verscheidene aangebrachte veranderingen worden terug gezet naar oorsponkelijke waarden.\\\\nTerugzetten?\\\\n\n\nsave_Def = Als Basis opslaan\n\nsd_tip = Slaat de huidige waarden als basis voor nieuwe Afspraken op\n\nselectdb = Database is niet aangegeven. Geef plaats database op.\n\nset_def_font = Standaardlettertype instellen\n\nsplash = Geef Startbeeld\n\nsrch = Zoeken\n\nstackonerr = Geef Stack-Trace-Option aan bij foutmelding\n\ntodomissingdata = Te Doen-Tekst en Datum invoeren.\n\ntodoquickentry = Te Doen-Snelinvoer*\n\ntth = Dins-Donder\n\nuncategorized = <Geen Categorie>\n\nviewchglog = Wijzig Log aanduiding\n\nweekdays = Maandag - Vrijdag \n\nweekends = Zaterdag en Zondag\n\nweekly = weekelijks\n\nwhite = Wit\n\nyearly = jaarlijks\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_nl_BE.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NIEUWE AFSPRAAK *****\n\n-db_argument_is_missing = -db argument ontbreekt\n\nAbout = Over\n\nAbout_BORG = Over BORG\n\nAction = Actie\n\nAdd = Toevoegen\n\nAddCat = Nieuw categorie maken:\n\nAdd_Log = Logboek Item toevoegen\n\nAdd_New = Nieuwe invoer\n\nAdd_State = Toestand toevoegen\n\nAdd_Type = Type toevoegen\n\nAddress = Adres\n\nAddress_Book = Adresboek\n\nAddress_Book_Entry = Invoer adresboek\n\nAfter = Na\n\nAll = Alles\n\nAll_Open = Allemaal geopend\n\nApply_DB_Change = Database-wijziging toepassen\n\nAppointment_Editor = Afspraak-editor\n\nAppointment_Editor_for_ = Afspraakplanner voor \n\nBefore = Voor\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Verjaardag\n\nBoxing_Day = Tweede kerstdag\n\nBrowse = Zoeken\n\nBuild_Time\\:_ = \\\\nBuild Datum & Tijd:\\\\n\n\nCLOSED = AFGEWERKT\n\nCancel = Annuleer\n\nCategories = Categorie\n\nCategory = Categorie\n\nCell_Phone\\: = Mobiele Telefoon:\n\nChange = Wijzigen\n\nChristmas = Kerstmis\n\nClear_DueDate = Vervaldag wissen \n\nClone = Dupliceren\n\nClose = Afsluiten\n\nClosed = Afgewerkt\n\nColor = Kleur\n\nCompany = Bedrijf\n\nConfirm_DB_Change = database-wijziging bevestigen\n\nConfirm_Delete = Bevestig verwijderen\n\nCopy = Kopi\\u00EBren\n\nCould_not_parse_times\\:_ = Tijden niet verwerkt: \n\nDataBase_Directory = Database map\n\nDatabaseInformation = Database informatie\n\nDate = Datum\n\nDay_View = Dagoverzicht\n\nDaylight_Savings_Time = Zomertijd\n\nDays_Left = Resttijd\n\nDelete = Verwijderen\n\nDelete_Memo = Wis Memo\n\nDelete_One_Only = Eenmalig verwijderen\n\nDelete_State = Toestand verwijderen\n\nDelete_Type = Type verwijderen\n\nDescription = Beschrijving\n\nDirectory_[ = Map [\n\nDiscard_Text? = Tekst verwijderen?\n\nDismiss = Sluiten\n\nDone_(Delete) = Gedaan (wissen)\n\nDone_(No_Delete) = Gedaan (niet wissen)\n\nDueDate = Vervaldag\n\nDue_Date = Vervaldag\n\nEdit = Wijzigen\n\nEditNew = Nieuw\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = Email instellingen\n\nEnable_Email = Email gebruiken\n\nEndDate = Einddatum\n\nEnter_Log = Logboek Item openen\n\nEnter_Memo_Name = Geef een naam op voor de nieuwe memo\n\nExisting_Memo = Er bestaat reeds een memo met deze naam\n\nExit = Sluiten\n\nFather's_Day = Vaderdag\n\nFax\\: = Fax:\n\nFilter\\: = Filter:\n\nFirst = Voornaam\n\nFirst_Name\\: = Voornaam:\n\nFrequency = Frequentie\n\nGo_To = Ga naar...\n\nHalf_Day = Halve dag\n\nHalloween = Halloween\n\nHelp = Help\n\nHoliday = Feestdag\n\nHomeAddress = Adres thuis\n\nHome_City\\: = Plaats:\n\nHome_Country\\: = Land:\n\nHome_Phone = Telefoon thuis\n\nHome_Phone\\: = Telefoon thuis:\n\nHome_State\\: = Provincie:\n\nHome_Street_Address = Straat\n\nHome_Zip_Code\\: = Postcode:\n\nHour = Uur\n\nHours = Uren\n\nISO_week_number = Gebruik ISO 8601 voor Weeknummering\n\nImport = Importeer\n\nImport_WARNING = Import WAARSCHUWING\n\nImporting_ = Importeer\n\nIndependence_Day = Onafhankelijkheidsdag\n\nInitializing = Initialiseren\n\nItem = Item\n\nItem_ = Invoer\n\nItem_\\# = Item #\n\nLabor_Day = Dag van de arbeid\n\nLabour_Day_(Can) = Dag van de arbeid (Can)\n\nLast = Familienaam\n\nLast_Name\\: = Familienaam:\n\nLicense = Gebruiksovereenkomst\n\nLook_and_Feel\\: = Uitzicht:\n\nMemo_Name = Memo Naam\n\nMemos = Memo's\n\nMinute = Minuut\n\nMinutes = Minuten\n\nMonth_View = Maandoverzicht\n\nMother's_Day = Moederdag\n\nMove_To_Following_Day = Zet naar volgende dag|Z\n\nNEW_Item = NIEUWE invoer\n\nNew_Memo = Nieuwe Memo\n\nNew_State = Nieuwe toestand\n\nNew_Task_Type = Nieuw taaktype\n\nNew_Year's_Day = Nieuwjaar\n\nNext_States = Volgende toestand\n\nNickname\\: = Bijnaam:\n\nNoOpenState = Error: Geen standaardwaarde gevonden voor type taak \n\nNo_Specific_Time = Geen tijd opgegeven\n\nNo_more = Geen herinneringen meer voor deze afspraak\n\nNotes = Notas\n\nNotice = Bericht\n\nNow = -- Nu! --\n\nOK = OK\n\nOPEN = OPEN\n\nOpen = Open\n\nOpen_Calendar = Open kalender\n\nOptions = Opties\n\nPA = Persoon\n\nPager\\: = Mobiel:\n\nPassword = Paswoord\n\nPlease_choose_File_to_Import_From = Kies te importeren bestand\n\nPlease_choose_directory_to_place_XML_files = Kies een doelmap voor de XML-bestanden\n\nPlease_enter_some_appointment_text = Omschrijving invoeren A.U.B.\n\nPlease_select_a_state = aub een toestand kiezen\n\nPlease_select_a_type = Een type selecteren aub\n\nPopup_Times = Popup tijden\n\nPri = Prioriteit\n\nPrint = Afdrukken\n\nPrint_Chooser = Printer-Keuze\n\nPrint_In_Color? = Printen in kleur\n\nPrivate = Priv\\u00E9\n\nProjectInformation = Project Informatie\n\nProperties = Eigenschappen\n\nReally_Delete_ = Echt verwijderen? \n\nReally_change_the_database? = de database echt wijzigen?\n\nReally_delete_number_ = Item # echt verwijderen? \n\nRecurrence = Terugkerend\n\nReminder = Herinnering\n\nReminder_Notice = Tekst herinnering individuele afspraak\n\nRename_State = hernoem toestand\n\nRename_Type = Type hernoemen\n\nReset_Task_States_to_Default = Standaardwaarden taken herstellen\n\nResolution = Oplossing\n\nRunSQL = SQL uitvoeren\n\nSMTP_Server = SMTP Server\n\nSMTP_password = SMTP Paswoord (Optioneel)\n\nSMTP_user = SMTP gebruiker (Optioneel)\n\nSave = Opslaan\n\nSave_Memo = Bewaar Memo\n\nScreen_Name\\: = Alias:\n\nSearchString = Zoekterm\n\nSearch_Results = Zoekresultaten\n\nSelect = Selecteren\n\nSelect_Memo_Warning = Om deze opdracht uit te voeren, moet een memo worden geselecteerd.\n\nSelect_initial_state = Selecteer standaardwaarde\n\nSelect_next_state = Volgende toestand selecteren\n\nSet_DueDate = Vervaldag instellen\n\nSet_Initial_State = Stel standaardwaarde in\n\nShow_Canadian_Holidays = Toon Canadese feestdagen\n\nShow_Private_Appointments = Toon priv\\u00E9 afspraken\n\nShow_Public_Appointments = Toon gewone afspraken\n\nShow_Stack_Trace = Toon Stack Trace\n\nShow_U.S._Holidays = Toon U.S. feestdagen\n\nStandard_Time = Standaard tijd\n\nStartDate = Begindatum\n\nStart_Date = Begindatum\n\nStart_Time\\: = Start Tijd:\n\nState_Change = Statuswijziging\n\nStates = Toestand\n\nStatus = Status\n\nSubTasks = SubTaken\n\nTaskInformation = Opdracht informatie\n\nTask_Created = Taak aangemaakt\n\nTask_State_Editor = Taak toestand editor\n\nTask_Types = Taaktypes\n\nText = Tekst\n\nTime = Tijd\n\nTimes = Aantal\n\nTo_Do = To Do\n\nTo_Do_List = To Do Lijst\n\nToday = Vandaag\n\nType = Type\n\nUse_24_hour_time_format = Gebruik 24 uur-tijdsaanduiding\n\nUse_system_beep = Gebruik systeem biep\n\nUserColorScheme = Eigen kleurinstellingen\n\nVacation = Vakantie\n\nValentine's_Day = Valentijnsdag\n\nWeb_Page\\: = Web Page:\n\nWeek_Starts_with_Monday = Week start op maandag\n\nWeek_View = Weekoverzicht\n\nWeek_View_End_Hour\\:_ = Week overzicht eind uur: \n\nWeek_View_Start_Hour\\:_ = Week overzicht  start uur: \n\nWorkAddress = Adres werk\n\nWork_City\\: = Plaats:\n\nWork_Country\\: = Land:\n\nWork_Phone = Telefoon werk\n\nWork_Phone\\: = Telefoon werk:\n\nWork_State\\: = Provincie:\n\nWork_Street_Address = Straat\n\nWork_Zip_Code\\: = Postcode:\n\nYour_Email_Address = Uw Email adres\n\n]_does_not_exist = ] bestaat niet\n\n]_is_not_a_directory = ] is geen map\n\n__through__ = tot\n\naddcat = Nieuwe categorie\n\naddresses = Adressen\n\nappearance = Voorkomen\n\napply = Toepassen\n\nappointments = Afspraken\n\nappt_error = Het afspraken venster wordt door een eerdere fout gesloten.||Mogelijk werd een Afspraak uit het To Do venster gesloten terwijl de Editor open stond. Zo niet, BORG opnieuw starten.\n\napptlist = Afsprakenlijst\n\nappttext = Uitleg bij de afspraak\n\nappttime = Afspraaktijd\n\nbad_db_2 = \\\\nAls een verkeerde bestandverwijzing de fout veroorzaakt, AUB 'JA' kiezen om te wijzigen.\n\nbiweekly = tweewekelijks\n\nblack = zwart\n\nblue = blauw\n\ncalShowSubtask = Toon Subtaken in Kalender/To Do\n\ncalShowTask = Toon taken in Kalender/To Do\n\ncase_sensitive = Hoofdlettergevoelig\n\ncat_choose = Kies categorie\n\ncatchooser = Categorie kiezen\n\nchangedate = Datum aanpassen\n\nchg_cat = Verander van categorie\n\nchoose_file = Kies bestand\n\nchoosecat = Te tonen categorie(n) kiezen\n\nclear_all = Alles verwijderen\n\nclose_date = Sluit datum\n\nclose_proj_warn = Kan het project niet sluiten. Minstens \\u00E9\\u00E9n deeltaak is niet gesloten.\n\nconfirm_overwrite = bevestig overschrijven\n\ncontact = Contact-informatie\n\ncontributions_by = Werkten mee aan dit programma:\n\ncopy_appt = Afspraak kopi\\u00EBren\n\ncopyright = Auteursrecht\n\ncreated = Aangemaakt\n\ncustom_times_header = Popup herinneringstijd voor\n\ndaily = dagelijks\n\ndb_set_to = \\\\n\\\\nGebruikte bestandsmap: \n\ndel_tip = Verwijder afspraak / alle items van een wederkerende afspraak\n\ndelcat_warn = WAARSCHUWING! ALLE afspraken en taken voor deze categorie verwijderen\n\ndelete_cat = Categorie verwijderen\n\ndelete_cat_choose = Kies Categorie om te verwijderen\n\ndelete_selected = Geselecteerd verwijderen\n\ndeleted = Gewist\n\ndlist = Selecteer dagen\n\ndone = afgewerkt\n\ndoo_tip = Verwijder een wederkerende afspraak ENKEL op de GESELECTEERDE DATUM\n\ndview_font = Dagoverzicht lettertype\n\nedit_types = Taaktypes en -toestand bewerken\n\nelapsed_time = Verlopen tijd\n\nempty_desc = Geef een beschrijving op a.u.b.\n\nenable_popups = Popup herinnering toestaan\\n\n\nenable_systray = Icoon systeemvak inschakelen (Herstart noodzakelijk)\n\nenturl = Een URL ingeven:\n\nep = Voorkeuren bewerken\n\nexpXML = XML exporteren...\n\nexport = Exporteren\n\nfonts = Lettertypes\n\nforever = Eeuwig herhalen\n\ngreen = groen\n\nh2info = H2 Informatie\n\nhistory = Geschiedenis\n\nhsqldb = HSQLDB\n\nhsqldbinfo = HSQLDB Informatie\n\nimpXML = XML importeren...\n\nimpexpMenu = Importeer / Exporteer\n\njdbc = Generic JDBC\n\nlocale = Land (vereist herstart van het programma)\n\nmin_aft_app = minuten na de afspraak\n\nmin_bef_app = minuten voor de afspraak\n\nminute_reminder = minuten herinnering\n\nminutes_ago = minuten terug!\n\nmisc = Overige\n\nmonthly = maandelijks (datum)\n\nmonthly_day = maandelijks (dag)\n\nmview_font = Maandoverzicht (Print) lettertype\n\nmwf = Maan-, woens- & vrijdag\n\nnavy = marine\n\nndays = Om de <#> dagen\n\nnewDate\\: = Nieuwe Datum:\n\nnoOutput = SQL Commando resultaat leeg of fouten\n\nnummonths = Aantal maanden te printen?\n\nonce = \\u00E9\\u00E9nmalig\n\nopen_subtasks = Kan de taak niet afsluiten, Deeltaken zijn niet allemaal afgesloten\n\nopen_tasks = Open Taken\n\noverwrite_warning = Bestand bestaat. OK om bestand te overschrijven: \n\npopup_reminders = Popup herinnering\n\nprojdd_warning = Kan het project niet bewaren. Vervaldag van een deeltaak later dan deze van het project.\n\nproject = Project\n\nprojects = Projecten\n\nrecur_compat = Error: Herhaling is niet compatibel met de afspraakdatum\n\nred = rood\n\nremcat = Verwijder ongebruikte categorie(n)\n\nreminder_time = Tijd Email herinnering\n\nrepeating = Terugkerend\n\nreset_state_warning = Dit reset alle wijzigingen die je aan het taakmodel hebt aangebracht.\\\\nDe veranderingen worden teruggezet naar de oorspronkelijke waarden.\\\\nWil je werkelijk doorgaan?\\\\n\n\nrestore_defaults = Standaardwaarden herstellen\n\nrlsnotes = Versie info\n\nsave_Def = Bewaar als standaard\n\nsd_dd_warn = Fout: Vervaldag kan niet voor de startdatum liggen\n\nsd_tip = Bewaar huidige waarden als nieuwe standaard voor afspraken\n\nselect_all = Alles Selecteren\n\nselect_appt = Selecteer een afspraak\n\nselect_export_dir = Selecteer export-map\n\nselectdb = Database is niet opgegeven. De database instellingen opgeven aub.\n\nsend_reminder = Email herinnering zenden\n\nset_def_font = Standaard lettertype\n\nshow_rpt_num = Toon herhaal nummer\n\nshowdoy = Toon dag van het jaar\n\nsocket_port = Socket Port (-1=uitgeschakeld, vereist herstart)\n\nsocket_warn = Socket Port moet geheel getal zijn\n\nsplash = Toon opstartscherm\n\nsrch = Zoek\n\nstackonerr = Toon Stack Trace opties en -foutmeldingen\n\nstdd_warning = Kan de taak niet bewaren. Een deeltaak heeft een vervaldag, later dan de vervaldag van de taak.\n\nstrike = doorstreept\n\nstripecolor = Gekleurde streep in lijsten\n\nsubject = Onderwerp\n\nsubtask = Deeltaak\n\nsubtask_id = Deeltaak ID\n\ntaskOptions = Taakopties\n\ntask_abbrev = Toon het taaknummer in de kalender\n\ntaskdd_warning = Kan de taak niet bewaren.  Vervaldag van de taak later dan deze van het project.\n\ntasks = Taken\n\ntodomissingdata = AUB zowel To Do tekst als datum invoeren.\n\ntodoquickentry = To Do Snelinvoer *\n\ntotal_tasks = Totaal Taken\n\ntruncate_appts = Afspraken in maandoverzicht splitsen\n\ntth = Dins- & donderdag\n\nucolortext1 = Gebruik ingestelde kleuren in To Do lijst|T\n\nucolortext10 = Feestdagen\n\nucolortext11 = Verjaardagen\n\nucolortext12 = Standaard\n\nucolortext13 = Feestdag\n\nucolortext14 = Halve Dag\n\nucolortext15 = Vakantie\n\nucolortext16 = Vandaag\n\nucolortext17 = Weekend\n\nucolortext18 = Weekdag\n\nucolortext2 = Markeer To Do in maandoverzicht|M\n\nucolortext4 = Tekstkleur 1\n\nucolortext5 = Tekstkleur 2\n\nucolortext6 = Tekstkleur 3\n\nucolortext7 = Tekstkleur 4\n\nucolortext8 = Tekstkleur 5\n\nucolortext9 = taken\n\nuncategorized = <Geen Categorie>\n\nuser = gebruiker\n\nviewchglog = Bekijk log wijzigingen\n\nweekdays = Maandag tot vrijdag\n\nweekends = Zaterdag en zondag\n\nweekly = wekelijks\n\nwhite = wit\n\nwview_font = Weekoverzicht lettertype\n\nyearly = jaarlijks\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_pl.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NOWE ZDA\\u017BENIE *****\n\n-db_argument_is_missing = -db brakuje argumentu\n\nAbout = O programie\n\nAbout_BORG = O BORG\n\nAction = Dzia\\u0142anie\n\nAdd = Dodaj\n\nAddCat = Wprowad\\u017A now\\u0105 kategori\\u0119:\n\nAdd_New = Dodaj nowe\n\nAdd_State = Dodaj stan\n\nAdd_Type = Dodaj typ\n\nAddress = Adres\n\nAddress_Book = Ksi\\u0105\\u017Cka adresowa\n\nAddress_Book_Entry = Wpis ksia\\u017Cki adresowej\n\nAll = Wszystkie\n\nApply_DB_Change = Zastosuj zmiany do DB\n\nAppointment_Editor = Edytor zdarze\\u0144\n\nAppointment_Editor_for_ = Edytor zdarze\\u0144 dla \n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Urodziny\n\nBoxing_Day = Boxing Day\n\nBrowse = Przegl\\u0105daj\n\nBuild_Time\\:_ = \\\\nCzas Build'a:\\\\n\n\nCLOSED = ZAMKNI\\u011ATE\n\nCanada_Day = Canada Day\n\nCancel = Anuluj\n\nCategories = Kategorie\n\nCategory = Kategoria\n\nCell_Phone\\: = Kom\\u00F3rka:\n\nChange = Zmie\\u0144\n\nChristmas = Bo\\u017Ce narodzenie\n\nCivic_Holiday = Civic Holiday\n\nClone = Klonuj\n\nClose = Zamknij\n\nClosed = Zamkni\\u0119te\n\nColor = Kolor\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = Firma\n\nConfirm_DB_Change = Potwierd\\u017A zmian\\u0119 DB\n\nConfirm_Delete = Potwierd\\u017A usuwanie\n\nCould_not_parse_times\\:_ = Nie mo\\u017Cna zanalizowa\\u0107 czas\\u00F3w (Could not parse times): \n\nDataBase_Directory = Katalog bazy danych\n\nDatabaseInformation = Informacje bazy danych\n\nDate = Data\n\nDay_View = Widok dnia\n\nDaylight_Savings_Time = Daylight Savings Time\n\nDays_Left = Pozosta\\u0142o Dni\n\nDelete = Skasuj\n\nDelete_One_Only = Skasuj tylko jedno\n\nDelete_State = Skasuj stan\n\nDelete_Type = Skasuj typ\n\nDescription = Opis\n\nDirectory_[ = Katalog [\n\nDiscard_Text? = Odrzuci\\u0107 Tekst?\n\nDismiss = Odrzu\\u0107\n\nDone_(Delete) = Zrobione (Kasowa\\u0107)\n\nDone_(No_Delete) = Zrobione (Nie kasowa\\u0107)\n\nDueDate = DataKo\\u0144ca\n\nDue_Date = Data Zako\\u0144czenia\n\nEdit = Edytuj\n\nEditNew = Edytuj nowe\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = Parametry Email\n\nEnable_Email = W\\u0142\\u0105cz Email\n\nExit = Wyj\\u015Bcie\n\nFather's_Day = Dzie\\u0144 Ojca\n\nFax\\: = Fax:\n\nFilter\\: = Filtr:\n\nFirst = Imi\\u0119\n\nFirst_Name\\: = Imi\\u0119:\n\nFrequency = Cz\\u0119stotliwo\\u015B\\u0107\n\nGo_To = Id\\u017A do\n\nGround_Hog_Day = Ground Hog Day\n\nHalf_Day = praca tylko przed po\\u0142udniem/tylko po po\\u0142udniu\n\nHalloween = Halloween\n\nHelp = Pomoc\n\nHoliday = \\u015Awi\\u0119to\n\nHomeAddress = Adres Domowy\n\nHome_City\\: = Miasto:\n\nHome_Country\\: = Pa\\u0144stwo:\n\nHome_Phone = Telefon domowy\n\nHome_Phone\\: = Telefon domowy:\n\nHome_State\\: = Wojew\\u00F3dztwo:\n\nHome_Street_Address = Ulica\n\nHome_Zip_Code\\: = Kod pocztowy:\n\nImport = Importuj\n\nImport_WARNING = OSTRZE\\u017BENIE importu\n\nImporting_ = Importuje \n\nIndependence_Day = Dzie\\u0144 Niepodleg\\u0142o\\u015Bci\n\nInitializing = Inicjalizacja\n\nItem = Pozycja\n\nItem_ = Pozycja \n\nItem_\\# = Pozycja #\n\nLabor_Day = Labor Day\n\nLabour_Day_(Can) = Labour Day (Can)\n\nLast = Nazwisko\n\nLast_Name\\: = Nazwisko:\n\nLicense = Licencja\n\nLook_and_Feel\\: = Wygl\\u0105d:\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemorial_Day = Memorial Day\n\nMonth_View = Widok miesi\\u0119cy\n\nMother's_Day = Dzie\\u0144 Matki\n\nNEW_Item = NOWA pozycja\n\nNew_State = Nowy stan\n\nNew_Task_Type = Nowy typ zada\\u0144\n\nNew_Year's_Day = Nowy Rok\n\nNext_States = Nast\\u0119pne stany\n\nNickname\\: = Pseudonim:\n\nNo_Specific_Time = Brak okre\\u015Blonego czasu\n\nNotes = Notatki/Uwagi\n\nNotice = Zawiadomienie (notice)\n\nOK = OK\n\nOpen = Otw\\u00F3rz\n\nOpen_Calendar = Otw\\u00F3rz kalendarz\n\nOptions = Opcje\n\nPA = PA\n\nPager\\: = Pager:\n\nPassword = Has\\u0142o\n\nPlease_choose_File_to_Import_From = Prosz\\u0119 wybra\\u0107 plik z kt\\u00F3rego b\\u0119dziesz importowa\\u0142\n\nPlease_choose_directory_to_place_XML_files = Prosz\\u0119 wybra\\u0107 katalog dla plik\\u00F3w XML\n\nPlease_enter_some_appointment_text = Prosz\\u0119 wprowad\\u017A jak\\u0105\\u015B tre\\u015B\\u0107 zdarzenia\n\nPlease_select_a_state = Prosz\\u0119 wybra\\u0107 stan\n\nPlease_select_a_type = Prosz\\u0119 wybra\\u0107 typ\n\nPresidents_Day = Presidents Day\n\nPri = Pri\n\nPrint = Drukuj\n\nPrint_Chooser = Wyb\\u00F3r drukarki\n\nPrint_In_Color? = Drukowa\\u0107 w kolorze?\n\nPrivate = Prywatne\n\nProperties = W\\u0142a\\u015Bciwo\\u015Bci\n\nReally_Delete_ = Naprawd\\u0119 skasowa\\u0107\n\nReally_change_the_database? = Naprawd\\u0119 zmieni\\u0107 baz\\u0119 danych?\n\nReally_delete_number_ = Naprawd\\u0119 skasowa\\u0107 numer \n\nRecurrence = Powtarzanie\n\nRemembrance_Day = Remembrance Day\n\nRename_State = Zmie\\u0144 nazw\\u0119 stanu\n\nRename_Type = Zmie\\u0144 nazw\\u0119 typu\n\nReset_Task_States_to_Default = Przywr\\u00F3\\u0107 list\\u0119 zada\\u0144 do ust. domy\\u015Blnych\n\nResolution = Rozdzielczo\\u015B\\u0107\n\nSMTP_Server = Serwer SMTP\n\nSave = Zapisz\n\nScreen_Name\\: = Nazwa wy\\u015Bwietlana:\n\nSearch_Results = Wyniki szukania\n\nSelect = Wybierz\n\nSelect_next_state = Wybierz nast\\u0119pny stan\n\nShow_Canadian_Holidays = Pokazuj Kanadyjskie \\u015Bwi\\u0119ta\n\nShow_Private_Appointments = Poka\\u017C prywatne zdarzenia\n\nShow_Public_Appointments = Poka\\u017C publiczne zdarzenia\n\nShow_Stack_Trace = Poka\\u017C \\u015Bledzenie stosu (Show Stack Trace)\n\nShow_U.S._Holidays = Poka\\u017C \\u015Bwi\\u0119ta U.S.\n\nSt._Patrick's_Day = St. Patrick's Day\n\nStandard_Time = Czas Standardowy\n\nStartDate = DataStartu\n\nStart_Date = Data Rozpocz\\u0119cia\n\nStart_Time\\: = Czas Rozpocz\\u0119cia:\n\nStates = Stany\n\nStatus = Status\n\nSubTasks = Podpunkty\n\nTaskInformation = Informacje zada\\u0144\n\nTask_State_Editor = Edytor stan\\u00F3w zada\\u0144\n\nTask_Types = Typy zada\\u0144\n\nText = Tekst\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = Czas\n\nTimes = Razy\n\nTo_Do = Do zrobienia\n\nTo_Do_List = Lista \"do zrobienia\"\n\nToday = Dzisiaj\n\nType = Typ\n\nUse_24_hour_time_format = U\\u017Cyj 24 godzinnego formatu czasu\n\nVacation = Wakacje/urlop\n\nValentine's_Day = Walentynki\n\nVeteran's_Day = Veteran's Day\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = Strona WWW:\n\nWeek_Starts_with_Monday = Tydzie\\u0144 zaczyna si\\u0119 od Poniedzia\\u0142ku\n\nWeek_View = Widok tygodnia\n\nWeek_View_End_Hour\\:_ = Widok tygodnia - Godzina Zako\\u0144czenia: \n\nWeek_View_Start_Hour\\:_ = Widok tygodnia - Godzina Rozpocz\\u0119cia: \n\nWorkAddress = Adres do Pracy\n\nWork_City\\: = Miasto:\n\nWork_Country\\: = Pa\\u0144stwo:\n\nWork_Phone = Telefon do pracy\n\nWork_Phone\\: = Telefon do pracy:\n\nWork_State\\: = Wojew\\u00F3dztwo:\n\nWork_Street_Address = Ulica\n\nWork_Zip_Code\\: = Kod pocztowy:\n\nYour_Email_Address = Tw\\u00F3j adres Email\n\n]_does_not_exist = ] nie istnieje\n\n]_is_not_a_directory = ] nie jest katalogiem\n\n__through__ = przez (through)\n\naddcat = Dodaj now\\u0105 kategori\\u0119\n\nappearance = Wygl\\u0105d\n\napply = Zastosuj\n\nappt_error = Okno zdarze\\u0144 zostanie zamkni\\u0119te w wyniku wcze\\u015Bniejszego b\\u0142\\u0119du. By\\u0107 mo\\u017Ce zdarzenie zosta\\u0142o usuni\\u0119te z listy \"do zrobienia\" kiedy edytor by\\u0142 otwarty. Je\\u017Celi nie, uruchom ponownie Borg\n\napptlist = Lista zdarze\\u0144\n\nappttext = Tekst zdarzenia\n\nappttime = Czas zdarzenia\n\nbad_db_2 = \\\\nJe\\u015Bli z\\u0142y adres bazy powoduje b\\u0142\\u0119dy, prosz\\u0119 wybra\\u0107 TAK, aby go zmieni\\u0107.\n\nbiweekly = dwutygodniowo\n\nblack = czarny\n\nblue = niebieski\n\ncatchooser = Wybierz kategori\\u0119\n\nchangedate = Zmie\\u0144 dat\\u0119\n\nchoose_file = Wybierz plik\n\nchoosecat = Wybierz kategorie do wy\\u015Bwietlenia\n\nclear_all = Wyczy\\u015B\\u0107 wszystko\n\ncontact = Informacje kontaktu\n\ndaily = dziennie\n\ndb_set_to = \\\\n\\\\nTwoja baza danych jest ustawiona na: \n\ndel_tip = Skasuj wszystkie wyst\\u0105pienia (powtarzania) zdarzenia\n\ndelete_selected = Usu\\u0144 zaznaczone\n\ndoo_tip = Skasuj tylko aktualne wyst\\u0105pienie powtarzaj\\u0105cego si\\u0119 zdarzenia\n\nedit_types = Edytuj typy zada\\u0144 i stan\\u00F3w\n\nenable_popups = W\\u0142\\u0105cz przypomnienie Popup\\n\n\nenturl = Wprowad\\u017A URL:\n\nep = Edytuj Ustawienia\n\nexpXML = Eksportuj XML...\n\nforever = Powtarzaj w niesko\\u0144czono\\u015B\\u0107\n\ngreen = zielony\n\nimpXML = Importuj XML...\n\nimpexpMenu = Import / Eksport\n\nlocale = Lokalizacja (wymaga restartu programu)\n\nmin_aft_app = minut po zdarzeniu\n\nmin_bef_app = minut przed zdarzeniem\n\nmisc = R\\u00F3\\u017Cne\n\nmonthly = miesi\\u0119cznie (data)\n\nmonthly_day = miesi\\u0119czne (dzie\\u0144)\n\nmwf = Po\\u0144-\\u015Ar-Pi\n\nndays = Ka\\u017Cde N dni\n\nnewDate\\: = Nowa data:\n\nnummonths = Liczba miesi\\u0119cy do drukowania?\n\nonce = raz\n\npopup_reminders = Przypomnianie Popup\n\nred = czerwony\n\nremcat = Usu\\u0144 nieu\\u017Cywane kategorie\n\nreset_state_warning = To zresetuje wszystkie Twoje ustawienia wygl\\u0105du dla wzoru zada\\u0144 (task model)\\\\nWz\\u00F3r zada\\u0144 zostanie zresetowany do ustawie\\u0144 domy\\u015Blnych.\\\\nCzy naprawd\\u0119 chcesz to zrobi\\u0107?\\\\n\n\nsave_Def = Zapisz domy\\u015Blne\n\nsd_tip = Zapisz obecne warto\\u015Bci jako domy\\u015Blne dla zdarze\\u0144\n\nselect_all = Zaznacz wszystko\n\nselectdb = Baza danych nie jest ustawiona. Prosz\\u0119 wprowadzi\\u0107 ustawienia bazy danych.\n\nset_def_font = Ustaw domy\\u015Bln\\u0105 czcionk\\u0119\n\nshowdoy = Poka\\u017C dzie\\u0144 roku\n\nsplash = Poka\\u017C splash screen\n\nsrch = Szukaj\n\nstackonerr = Poka\\u017C opcje Stack Trace w dialogach b\\u0142\\u0119du\n\nstrike = przekre\\u00C5\\u009Blony (strike-through)\n\ntodomissingdata = Prosz\\u0119 poda\\u0107 oba: tekst \"do zrobienia\" i dat\\u0119.\n\ntodoquickentry = Szybki wpis \"do zrobienia\" *\n\ntth = Wt-Czw\n\nuncategorized = <Brak kategorii>\n\nviewchglog = Zobacz log zmian\n\nweekdays = Poniedzia\\u0142ek-Pi\\u0105tek\n\nweekends = Sobota i niedziela\n\nweekly = tygodniowo\n\nwhite = bia\\u0142y\n\nyearly = rocznie\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_pt.properties",
    "content": "\n*****_NEW_APPT_***** = ***** NOVO EVENTO *****\n\n-db_argument_is_missing = -falta argumento de banco de dados\n\nAbout = Sobre|S\n\nAbout_BORG = Sobre BORG\n\nAction = A\\u00E7\\u00E3o|C\n\nAdd = Novo|N|INSERT\n\nAddCat = Digite Nova Categoria:\n\nAdd_Log = Adicionar Entrada de Registro (Log)\n\nAdd_New = Adicionar Novo|N|INSERT\n\nAdd_State = Adicionar Estado\n\nAdd_Type = Adicionar Tipo\n\nAddress = Endere\\u00E7o\n\nAddress_Book = Agenda de Endere\\u00E7os|A|ctrl A\n\nAddress_Book_Entry = Entrada da Agenda de Endere\\u00E7os\n\nAfter = Depois\n\nAll = Todas|T\n\nAll_Open = Todos Abertos\n\nApply_DB_Change = Realize modifica\\u00E7\\u00E3o no BD|C\n\nAppointment_Editor = Editor de Eventos\n\nAppointment_Editor_for_ = Editor de Eventos para\n\nBefore = Antes\n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = Anivers\\u00E1rio|V\n\nBoxing_Day = Boxing Day\n\nBrowse = Navegar|N\n\nBuild_Time\\:_ = \\\\nHora da Compila\\u00E7\\u00E3o:\\\\n\n\nCLOSED = FECHADO\n\nCanada_Day = Canada Day\n\nCancel = Cancelar\n\nCategories = Categorias|R\n\nCategory = Categoria|R\n\nCell_Phone\\: = Telefone Celular:\n\nChange = Modificar|M|ctrl E\n\nChristmas = Natal\n\nCivic_Holiday = Civic Holiday\n\nClear_DueDate = Lipar Data de Fechamento\n\nClone = Clonar|O|ctrl O\n\nClose = Fechar|F|ctrl X\n\nClosed = Encerrada|E\n\nColor = Cor|C\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = Companhia|C\n\nConfirm_DB_Change = Confirme Modifica\\u00E7\\u00F5es no BD\n\nConfirm_Delete = Confirmar Dele\\u00E7\\u00E3o\n\nCould_not_parse_times\\:_ = N\\u00E3o pude ler repeti\\u00E7\\u00F5es:\n\nDataBase_Directory = Diret\\u00F3rio do Banco de Dados|D\n\nDatabaseInformation = Informa\\u00E7\\u00E3o do Banco de Dados\n\nDate = Data|D\n\nDay_View = Visualiza\\u00E7\\u00E3o Di\\u00E1ria\n\nDaylight_Savings_Time = Hor\\u00E1rio de Ver\\u00E3o\n\nDays = Dias\n\nDays_Left = Dias Restantes\n\nDelete = Deletar|D|DELETE\n\nDelete_Addresses = Delete Endere\\u00E7o(s)?\n\nDelete_Memo = Deletar Memo\n\nDelete_One_Only = Deletar um Apenas|U\n\nDelete_State = Deletar estado\n\nDelete_Type = Deletar Tipo\n\nDescription = Descri\\u00E7\\u00E3o\n\nDirectory_[ = Diret\\u00F3rio [\n\nDiscard_Text? = Descartar Texto?\n\nDismiss = Cancelar||alt X\n\nDone_(Delete) = Feito (Delete)|E\n\nDone_(No_Delete) = Feito (N\\u00E3o Delete)|O\n\nDueDate = Data de T\\u00E9rmino\n\nDue_Date = Data de t\\u00E9rmino|T\n\nEdit = Editar|E\n\nEditNew = Edita Novo\n\nEmail = Email\n\nEmail\\: = Email:|E\n\nEmailParameters = Par\\u00E2metros de Email\n\nEnable_Email = Habilite Email|E\n\nEndDate = DataFinal\n\nEnter_Log = Digite Entrada de Registro (log)\n\nEnter_Memo_Name = Favor digitar o nome para o novo memo\n\nExisting_Memo = J\\u00E1 existe um memo com esse nome\n\nExit = Sair|X|alt X\n\nFather's_Day = Father's Day\n\nFax\\: = Fax:|A\n\nFilter\\: = Filtro:|F\n\nFirst = Primeiro\n\nFirst_Name\\: = Nome:|F\n\nFrequency = Frequ\\u00EAncia|F\n\nGo_To = Ir Para|P\n\nGround_Hog_Day = Ground Hog Day\n\nHalf_Day = Meio Expediente|M\n\nHalloween = Halloween\n\nHelp = Ajuda|A|F1\n\nHoliday = Feriado\n\nHomeAddress = Endere\\u00E7o Residencial\n\nHome_City\\: = Cidade:|C\n\nHome_Country\\: = Pa\\u00EDs:|U\n\nHome_Phone = Tel Residencial\n\nHome_Phone\\: = Tel Residencial:|H\n\nHome_State\\: = Estado:|T\n\nHome_Street_Address = Endere\\u00E7o|A\n\nHome_Zip_Code\\: = CEP:|Z\n\nHour = Hora\n\nHours = Horas\n\nISO_week_number = Use Numera\\u00E7\\u00E3o de Semana ISO 8601\n\nImport = Importar\n\nImport_WARNING = AVISO de importa\\u00E7\\u00E3o\n\nImporting_ = Importando\n\nIndependence_Day = Dia da Independ\\u00EAncia (USA)\n\nInitializing = Inicializando\n\nItem = \\u00CDtem\n\nItem_ = \\u00CDtem\n\nItem_\\# = \\u00CDtem #|I\n\nLabor_Day = Labor Day\n\nLabour_Day_(Can) = Labour Day (Can)\n\nLast = \\u00DAltimo\n\nLast_Name\\: = Sobrenome:|L\n\nLicense = Licen\\u00E7a|L\n\nLook_and_Feel\\: = Apar\\u00EAncia:|L\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemo_Name = Nome do Memo\n\nMemorial_Day = Memorial Day\n\nMemos = Memos\n\nMinute = Minuto\n\nMinutes = Minutos\n\nMonth_View = Visualiza\\u00E7\\u00E3o Mensal\n\nMother's_Day = Mother's Day\n\nMove_To_Following_Day = Mova para o Seguinte Dia|M\n\nNEW_Item = Novo \\u00CDtem\n\nNew_Memo = Novo Memo\n\nNew_State = Novo Estado\n\nNew_Task_Type = Novo Tipo de Tarefa\n\nNew_Year's_Day = Ano Novo\n\nNext_States = Pr\\u00F3ximos Estados\n\nNickname\\: = Apelido:|N\n\nNoOpenState = Erro: N\\u00E3o foi encontrado nenhum estado inicial para o tipo de tarefa\n\nNo_Specific_Time = Sem Hora Espec\\u00EDfica|S\n\nNo_more = N\\u00E3o resta lembretes para este evento\n\nNotes = Anota\\u00E7\\u00F5es\n\nNotice = Aten\\u00E7\\u00E3o\n\nNow = -- Agora! --\n\nOK = OK\n\nOPEN = ABERTO\n\nOpen = Abrir|N\n\nOpen_Calendar = Abrir Calend\\u00E1rio|C\n\nOptions = Op\\u00E7\\u00F5es|O|ctrl O\n\nPA = PA|A\n\nPager\\: = Pager:|P\n\nPassword = Senha|P\n\nPlease_choose_File_to_Import_From = Por Favor Escolha o Arquivo a ser Importado\n\nPlease_choose_directory_to_place_XML_files = Favor escolher o diret\\u00F3rio a colocar os arquivos XML\n\nPlease_enter_some_appointment_text = Por Favor Digite Algum Texto de Evento\n\nPlease_select_a_state = Favor selecionar um estado\n\nPlease_select_a_type = Favor selecionar um tipo\n\nPopup_Times = Total de Popups\n\nPresidents_Day = Presidents Day\n\nPri = Pri|P\n\nPrint = Imprimir|P|ctrl P\n\nPrint_Chooser = Selecionador de Impressora\n\nPrint_In_Color? = Imprimir colorido?|C\n\nPrivate = Particular|P\n\nProjectInformation = Informa\\u00E7\\u00E3o do Projeto\n\nProperties = Propriedades\n\nReally_Delete_ = Realmente Deletar\n\nReally_change_the_database? = Realmente modificar o banco de dados?\n\nReally_delete_number_ = Realmente delete n\\u00FAmero\n\nRecurrence = Recorr\\u00EAncia\n\nRemembrance_Day = Remembrance Day\n\nReminder = Lembrete\n\nReminder_Notice = Aviso de Lembrete BORG\n\nRename_State = Renomear Estado\n\nRename_Type = Renomear Tipo\n\nReset_Task_States_to_Default = Resetar Estados de Tarefa ao Padr\\u00E3o|R\n\nResolution = Resolu\\u00E7\\u00E3o\n\nRunSQL = Execute SQL\n\nSMTP_Server = Servidor SMTP|S\n\nSMTP_password = Senha SMTP (Opcional)\n\nSMTP_user = Usu\\u00E1rio SMTP (Opcional)\n\nSave = Salvar|S|ctrl S\n\nSave_Memo = Salvar Memo\n\nScreen_Name\\: = Exibir Como:|M\n\nSearchString = Palavra-chave\n\nSearch_Results = Resultados da Pesquisa\n\nSelect = Selecione\n\nSelect_Memo_Warning = Um memo deve ser selecionado para que se possa realizar essa a\\u00E7\\u00E3o\n\nSelect_initial_state = Selecionar Initial State\n\nSelect_next_state = Selecionar pr\\u00F3ximo estado\n\nSet_DueDate = Configurar Data de Fechamento\n\nSet_Initial_State = Configure o Estado Inicial\n\nShow_Canadian_Holidays = Mostrar Feriados Canadenses|C\n\nShow_Private_Appointments = Mostrar Eventos Privados|V\n\nShow_Public_Appointments = Mostrar Eventos P\\u00FAblicos|P\n\nShow_Stack_Trace = Show Stack Trace\n\nShow_U.S._Holidays = Mostrar Feriados dos U.S.A.|U\n\nSt._Patrick's_Day = St. Patrick's Day\n\nStandard_Time = Hora Padr\\u00E3o\n\nStartDate = Data de In\\u00EDcio\n\nStart_Date = Data de in\\u00EDcio|D\n\nStart_Time\\: = In\\u00EDcio:|I\n\nState_Change = Estado modificado\n\nStates = Estados\n\nStatus = Estado|U\n\nSubTasks = Sub-tarefas\n\nTaskInformation = Informa\\u00E7\\u00E3o da Tarefa\n\nTask_Created = Tarefa Criada\n\nTask_State_Editor = Editor de Estado de Tarefa\n\nTask_Types = Tipos de Tarefas\n\nText = Texto\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = Hora\n\nTimes = Total|M\n\nTo_Do = Tarefas|T|ctrl T\n\nTo_Do_List = Lista A Fazer|F\n\nToday = Hoje|H|HOME\n\nType = Tipo|Y\n\nUse_24_hour_time_format = Utilizar formato de 24 horas|2\n\nUse_system_beep = Usar o Beep do Sistema\n\nUserColorScheme = Esquema de Cores Personalizada\n\nVacation = F\\u00E9rias|F\n\nValentine's_Day = Valentine's Day\n\nVeteran's_Day = Veteran's Day\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = Home-Page:|G\n\nWeek_Starts_with_Monday = Semana Inicia na Segunda-Feira|M\n\nWeek_View = Visualiza\\u00E7\\u00E3o Semanal\n\nWeek_View_End_Hour\\:_ = Hora Final do Dia:|E\n\nWeek_View_Start_Hour\\:_ = Hora Inicial do Dia:|S\n\nWorkAddress = Endere\\u00E7o Profissional\n\nWork_City\\: = Cidade:|I\n\nWork_Country\\: = Pa\\u00EDs:|R\n\nWork_Phone = Tel Profissional\n\nWork_Phone\\: = Tel Profissional:|W\n\nWork_State\\: = Estado:|E\n\nWork_Street_Address = Endere\\u00E7o|D\n\nWork_Zip_Code\\: = CEP:|O\n\nYour_Email_Address = Seu Email|Y\n\n]_does_not_exist = ] n\\u00E3o existe\n\n]_is_not_a_directory = ] n\\u00E3o \\u00E9 um diret\\u00F3rio\n\n__through__ = at\\u00E9\n\naddcat = Adicionar Nova Categoria|A\n\naddresses = Endere\\u00E7os\n\nappearance = Apar\\u00EAncia\n\napply = Aplicar|A\n\nappointments = Eventos\n\nappt_error = The appointment window will close due to a prior error. Perhaps an appointment was removed from the ToDo window while the editor was open. If not, restart Borg\n\napptlist = Lista de Eventos\n\nappttext = Texto do Evento\n\nappttime = Hor\\u00E1rio do Evento\n\nbad_db_2 = \\\\nSe um diret\\u00F3rio errado estiver causando erro, clique SIM para modific\\u00E1-lo.\n\nbiweekly = bi-semanalmente\n\nblack = preto\n\nblue = azul\n\ncalShowSubtask = Mostrar Sub-tarefas no Calend\\u00E1rio/Todo\n\ncalShowTask = Mostrar Tarefas no Calend\\u00E1rio/Todo\n\ncase_sensitive = Diferenciar mai\\u00FAsculas/min\\u00FAsculas\n\ncat_choose = Escolher Categoria\n\ncatchooser = Seletor de Categorias\n\nchangedate = Mudar Data|H\n\nchg_cat = Modoficar Categoria\n\nchoose_file = Escolher Arquivo\n\nchoosecat = Escolha as Categorias a serem Mostradas|C\n\nclear_all = Deletar Todos|D\n\nclose_date = Dia do Fechamento\n\nclose_proj_warn = N\\u00E3o posso fechar o projeto. Ao menos uma tarefa afiliada ainda n\\u00E3o foi fechada.\n\nconfirm_overwrite = Confirma Sobrescrever\n\ncontact = Informa\\u00E7\\u00E3o do Contato\n\ncontributions_by = Agradecimentos \\u00E0 Contribui\\u00E7\\u00F5es de:\n\ncopyright = Copyright\n\ncreated = Criada\n\ncustom_times_header = Total de Lembretes Pop-up para\n\ndaily = diariamente\n\ndb_set_to = \\\\n\\\\nSeu diret\\u00F3rio de dados \\u00E9:\n\ndel_tip = Deletar todas ocorr\\u00EAncias (repeti\\u00E7\\u00F5es) de um evento.\n\ndelcat_warn = AVISO! Deletando TODOS eventos e tarefas da categoria\n\ndelete_cat = Deletar Categoria|D\n\ndelete_cat_choose = Escolha Categoria para Dele\\u00E7\\u00E3o\n\ndelete_selected = Deletar Selecionado\n\ndeleted = Deletado\n\ndlist = selecionar dias\n\ndone = feito\n\ndoo_tip = Deletar somente a esta ocorr\\u00EAncia do evento recorrente.\n\ndview_font = Fonte da Visualiza\\u00E7\\u00E3o Di\\u00E1ria\n\nedit_types = Editar Tipos e Estados de Tarefas|E\n\nelapsed_time = Dias Gastos\n\nempty_desc = Favor digitar uma Descri\\u00E7\\u00E3o\n\nenable_popups = Habilitar Lembretes Pop-up\\n|P\n\nenable_systray = Habilite o \\u00CDcone do System Tray (necessita reinicializa\\u00E7\\u00E3o)\n\nenturl = Digite URL:\n\nep = Editar Prefer\\u00EAncias|E\n\nexpXML = Exportar XML...|E\n\nfonts = Fontes\n\nforever = Repetir Eternamente|F\n\ngreen = verde\n\nh2info = Informa\\u00E7\\u00E3o H2\n\nhistory = Hist\\u00F3rico\n\nhsqldb = HSQLDB\n\nhsqldbinfo = Informa\\u00E7\\u00E3o HSQLDB\n\nimpXML = Importar XML...|I\n\nimpexpMenu = Importar / Exportar|I\n\njdbc = JDBC Gen\\u00E9rico\n\nlocale = Locale (necess\\u00E1rio reiniciar o programa)|O\n\nmin_aft_app = minutos ap\\u00F3s o evento\n\nmin_bef_app = minutos antes do evento\n\nminute_reminder = lembrete em minutos\n\nminutes_ago = minutos atr\\u00E1s!\n\nmisc = Miscel\\u00E2nea\n\nmonthly = mensalmente (data)\n\nmonthly_day = mensalmente (dia)\n\nmview_font = Fonte de Impress\\u00E3o\n\nmwf = seg, qua e sex\n\nnavy = navy\n\nndays = a cada \"n\" dias\n\nnewDate\\: = Nova Data:|N\n\nnoOutput = O comando SQL n\\u00E3o emitiu nenhuma sa\\u00EDda ou erros\n\nnummonths = Total de meses a imprimir?\n\nonce = \\u00FAnica vez\n\nopen_subtasks = Imposs\\u00EDvel Fechar a Tarefa, nem todas as Subtarefas j\\u00E1 est\\u00E3o fechadas.\n\nopen_tasks = Terfas Abertas\n\noverwrite_warning = Arquivo Existe. Posso sobrescrev\\u00EA-lo:\n\npopup_reminders = Lembretes Pop-up\n\nprojdd_warning = N\\u00E3o \\u00E9 poss\\u00EDvel salvar o projeto. Uma tarefa afiliada tem um dia de fechamento posterior ao dia de fechamento do projeto\n\nproject = Projeto\n\nprojects = Projetos\n\nrecur_compat = Erro: Recorr\\u00EAncia n\\u00E3o \\u00E9 compat\\u00EDvel com a data do evento.\n\nred = vermelho\n\nremcat = Remover Categorias n\\u00E3o Utilizadas|R\n\nreminder_time = Hor\\u00E1rio do Email Lembrete|R\n\nrepeating = Repeti\\u00E7\\u00E3o\n\nreset_state_warning = Isso ir\\u00E1 desfazer qualquer personaliza\\u00E7\\u00E3o que tenha sido feita ao modelo de tarefa\\\\nO modelo de tarefa ser\\u00E1 reconfigurado como o modelo inicial padr\\u00E3o.\\\\nRealmente deseja continuar?\\\\n\n\nrestore_defaults = retorna ao padr\\u00E3o\n\nrlsnotes = Avisos da Vers\\u00E3o\n\nsave_Def = Salvar o Padr\\u00E3o\n\nsd_tip = Salvar valores atuais como novo Padr\\u00E3o para Eventos\n\nselect_all = Selecionar Todos|S\n\nselect_export_dir = Selecione Diret\\u00F3rio para Exporta\\u00E7\\u00E3o\n\nselectdb = Banco de Dados n\\u00E3o est\\u00E1 definido. Favor digitar as configura\\u00E7\\u00F5es do banco de dados.\n\nsend_reminder = Enviar Email Lembrete|E\n\nset_def_font = Fonte Padr\\u00E3o|D\n\nshow_rpt_num = Mostrar o N\\u00FAmero de Repeti\\u00E7\\u00F5es|W\n\nshowdoy = Mostrar Dia do Ano|Y\n\nsocket_port = Socket Port (-1=disabilitado, necessita reiniciar)\n\nsocket_warn = Socket Port tem que ser um inteiro\n\nsplash = Mostrar tela de inicializa\\u00E7\\u00E3o|S\n\nsrch = Pesquisar|P|F3\n\nstackonerr = Mostrar Op\\u00E7\\u00F5es do Stack Trace nas Janelas de Erro|K\n\nstdd_warning = Imposs\\u00EDvel Salvar Tarefa. Uma subtarefa tem um dia de fechamento posterior ao dia de fechamento da tarefa.\n\nstrike = Tachado\n\nstripecolor = Tabela de Cores (Table Stripe Color)\n\nsubject = Assunto\n\nsubtask = Subtarefa\n\nsubtask_id = ID da Subtarefa\n\ntaskOptions = Op\\u00E7\\u00F5es de Tarefas\n\ntask_abbrev = Mostrar N\\u00FAmero das Tarefas no Calend\\u00E1rio\n\ntaskdd_warning = N\\u00E3o \\u00E9 poss\\u00EDvel salvar tarefas com dia de fechamento posterior ao dia de fechamento do projeto\n\ntasks = Tarefas\n\ntodomissingdata = Por favor digite tanto o texto do A Fazer quanto a data.\n\ntodoquickentry = Entrada R\\u00E1pida de A Fazer *\n\ntotal_tasks = Tarefas Totais\n\ntruncate_appts = Abreviar Eventos na Visualiza\\u00E7\\u00E3o Mensal|T\n\ntth = ter e qui\n\nucolortext1 = usar cores personalizadas na lista A Fazer|T\n\nucolortext10 = feriados\n\nucolortext11 = anivers\\u00E1rios\n\nucolortext12 = padr\\u00E3o\n\nucolortext13 = feriado\n\nucolortext14 = meio-expediente\n\nucolortext15 = f\\u00E9rias\n\nucolortext16 = hoje\n\nucolortext17 = fim-de-semana\n\nucolortext18 = dia-de-semana\n\nucolortext2 = marcar A Fazer na visualiza\\u00E7\\u00E3o mensal|M\n\nucolortext4 = cor de texto 1\n\nucolortext5 = cor de texto 2\n\nucolortext6 = cor de texto 3\n\nucolortext7 = cor de texto 4\n\nucolortext8 = cor de texto 5\n\nucolortext9 = tarefas\n\nuncategorized = <Sem Categoria>\n\nuser = usu\\u00E1rio\n\nviewchglog = Visualizar o Registro de Mudan\\u00E7as|V\n\nweekdays = segunda-sexta\n\nweekends = s\\u00E1bado e domingo\n\nweekly = semanalmente\n\nwhite = branco\n\nwview_font = Fonte da Visualiza\\u00E7\\u00E3o Semanal\n\nyearly = anualmente\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_ru.properties",
    "content": "\n*****_NEW_APPT_***** = \\u041D\\u043E\\u0432\\u043E\\u0435 \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u0435\n\n-db_argument_is_missing = -db \\u0430\\u0440\\u0433\\u0443\\u043C\\u0435\\u043D\\u0442 \\u043D\\u0435 \\u0443\\u043A\\u0430\\u0437\\u0430\\u043D\n\nAbout = \\u041E...\n\nAbout_BORG = \\u041E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0435 BORG\n\nAction = \\u0414\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u0435\n\nAdd = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C\n\nAddCat = \\u0412\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u041D\\u043E\\u0432\\u0443\\u044E \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u044E:\n\nAdd_New = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C\n\nAdd_State = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nAdd_Type = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0422\\u0438\\u043F\n\nAddress = \\u0410\\u0434\\u0440\\u0435\\u0441\n\nAddress_Book = \\u0410\\u0434\\u0440\\u0435\\u0441\\u043D\\u0430\\u044F \\u041A\\u043D\\u0438\\u0433\\u0430\n\nAddress_Book_Entry = \\u0417\\u0430\\u043F\\u0438\\u0441\\u044C \\u0410\\u0434\\u0440\\u0435\\u0441\\u043D\\u043E\\u0439 \\u041A\\u043D\\u0438\\u0433\\u0438\n\nAll = \\u0412\\u0441\\u0435\n\nApply_DB_Change = \\u0417\\u0430\\u043F\\u043E\\u043C\\u043D\\u0438\\u0442\\u044C \\u0411\\u0414\n\nAppointment_Editor = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u043E\\u0440 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nAppointment_Editor_for_ = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u043E\\u0440 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F \\u0434\\u043B\\u044F \n\nBerger-Organizer_v = Berger-Organizer \\u0432\n\nBirthday = \\u0414\\u0420\n\nBoxing_Day = Boxing Day\n\nBrowse = \\u0421\\u043C\\u043E\\u0442\\u0440\\u0435\\u0442\\u044C\n\nBuild_Time\\:_ = \\\\n\\u0412\\u0440\\u0435\\u043C\\u044F \\u0421\\u0431\\u043E\\u0440\\u043A\\u0438:\\\\n\n\nCLOSED = \\u0417\\u0410\\u041A\\u0420\\u042B\\u0422\\u041E\n\nCanada_Day = Canada Day\n\nCancel = \\u041E\\u0442\\u043C\\u0435\\u043D\\u0430\n\nCategories = \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u0438\n\nCategory = \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u044F\n\nCell_Phone\\: = \\u043C\\u043E\\u0431\\u0438\\u043B\\u044C\\u043D\\u044B\\u0439 \\u0442\\u0435\\u043B\\u0435\\u0444\\u043E\\u043D:\n\nChange = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\n\nChristmas = \\u0420\\u043E\\u0436\\u0434\\u0435\\u0441\\u0442\\u0432\\u043E\n\nCivic_Holiday = Civic Holiday\n\nClone = \\u041A\\u043B\\u043E\\u043D\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\n\nClose = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C\n\nClosed = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u043E\n\nColor = \\u0426\\u0432\\u0435\\u0442\n\nColumbus_Day = Columbus Day\n\nCommonwealth_Day = Commonwealth Day\n\nCompany = \\u041A\\u043E\\u043C\\u043F\\u0430\\u043D\\u0438\\u044F\n\nConfirm_DB_Change = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0436\\u0434\\u0435\\u043D\\u0438\\u0435 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u044F \\u0411\\u0414\n\nConfirm_Delete = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0438\\u0442\\u044C \\u0423\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0435\n\nCould_not_parse_times\\:_ = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u043F\\u043E\\u043D\\u044F\\u0442\\u044C \\u0441\\u043A\\u043E\\u043B\\u044C\\u043A\\u043E \\u0440\\u0430\\u0437: \n\nDataBase_Directory = \\u041A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433 \\u0411\\u0414\n\nDatabaseInformation = \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F \\u043E \\u0411\\u0414\n\nDate = \\u0414\\u0430\\u0442\\u0430\n\nDay_View = \\u041F\\u0440\\u043E\\u0441\\u043C\\u043E\\u0442\\u0440 \\u0414\\u043D\\u044F\n\nDaylight_Savings_Time = Daylight Savings Time\n\nDays_Left = \\u041E\\u0441\\u0442\\u0430\\u043B\\u043E\\u0441\\u044C\n\nDelete = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\n\nDelete_One_Only = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u043E\\u0434\\u043D\\u043E\\u043A\\u0440\\u0430\\u0442\\u043D\\u043E\n\nDelete_State = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nDelete_Type = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0422\\u0438\\u043F\n\nDescription = \\u041E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435\n\nDirectory_[ = \\u041A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433 [\n\nDiscard_Text? = \\u041E\\u0442\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0432\\u0432\\u0435\\u0434\\u0435\\u043D\\u043D\\u044B\\u0439 \\u0442\\u0435\\u043A\\u0441\\u0442?\n\nDismiss = \\u0423\\u0431\\u0440\\u0430\\u0442\\u044C\n\nDone_(Delete) = \\u0413\\u043E\\u0442\\u043E\\u0432\\u043E (\\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C)\n\nDone_(No_Delete) = \\u0413\\u043E\\u0442\\u043E\\u0432\\u043E (\\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C)\n\nDueDate = \\u041F\\u043B\\u0430\\u043D\n\nDue_Date = \\u041F\\u043B\\u0430\\u043D\n\nEdit = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\n\nEditNew = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u041D\\u043E\\u0432\\u044B\\u0439\n\nEmail = \\u0415\\u043C\\u044D\\u0439\\u043B\n\nEmail\\: = \\u0415\\u043C\\u044D\\u0439\\u043B:\n\nEmailParameters = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u044B \\u041F\\u043E\\u0447\\u0442\\u044B\n\nEnable_Email = \\u0420\\u0430\\u0437\\u0440\\u0435\\u0448\\u0438\\u0442\\u044C \\u041F\\u043E\\u0447\\u0442\\u0443\n\nExit = \\u0412\\u044B\\u0445\\u043E\\u0434\n\nFather's_Day = Father's Day\n\nFax\\: = \\u0424\\u0430\\u043A\\u0441:\n\nFilter\\: = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440:\n\nFirst = \\u041F\\u0435\\u0440\\u0432\\u044B\\u0439\n\nFirst_Name\\: = \\u0418\\u043C\\u044F:\n\nFrequency = \\u0427\\u0430\\u0441\\u0442\\u043E\\u0442\\u0430\n\nGo_To = \\u041F\\u0435\\u0440\\u0435\\u0439\\u0442\\u0438 \\u041A\n\nGround_Hog_Day = \\u0414\\u0435\\u043D\\u044C \\u0421\\u0443\\u0440\\u043A\\u0430\n\nHalf_Day = \\u041F\\u043E\\u043B\\u0434\\u043D\\u044F\n\nHalloween = \\u0425\\u044D\\u043B\\u043B\\u043E\\u0443\\u0438\\u043D\\u043D\n\nHelp = \\u0421\\u043F\\u0440\\u0430\\u0432\\u043A\\u0430\n\nHoliday = \\u041F\\u0440\\u0430\\u0437\\u0434\\u043D\\u0438\\u043A\n\nHomeAddress = \\u0414\\u043E\\u043C\\u0430\\u0448\\u043D\\u0438\\u0439 \\u0410\\u0434\\u0440\\u0435\\u0441\n\nHome_City\\: = \\u0413\\u043E\\u0440\\u043E\\u0434:\n\nHome_Country\\: = \\u0421\\u0442\\u0440\\u0430\\u043D\\u0430:\n\nHome_Phone = \\u0414\\u043E\\u043C\\u0430\\u0448\\u043D\\u0438\\u0439 \\u0442\\u0435\\u043B.\n\nHome_Phone\\: = \\u0414\\u043E\\u043C\\u0430\\u0448\\u043D\\u0438\\u0439 \\u0442\\u0435\\u043B.:\n\nHome_Street_Address = \\u0410\\u0434\\u0440\\u0435\\u0441\n\nHome_Zip_Code\\: = \\u0418\\u043D\\u0434\\u0435\\u043A\\u0441:\n\nImport = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\n\nImport_WARNING = \\u041F\\u0420\\u0415\\u0414\\u0423\\u041F\\u0420\\u0415\\u0416\\u0414\\u0415\\u041D\\u0418\\u0415 \\u043E\\u0431 \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442\\u0435\n\nImporting_ = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u0443\\u044E\\u0442\\u0441\\u044F \n\nIndependence_Day = \\u0414\\u0435\\u043D\\u044C \\u041D\\u0435\\u0437\\u0430\\u0432\\u0438\\u0441\\u0438\\u043C\\u043E\\u0441\\u0442\\u0438\n\nInitializing = \\u0418\\u043D\\u0438\\u0446\\u0438\\u0430\\u043B\\u0438\\u0437\\u0438\\u0440\\u0443\\u0435\\u0442\\u0441\\u044F\n\nItem = \\u041F\\u0443\\u043D\\u043A\\u0442\n\nItem_ = \\u041F\\u0443\\u043D\\u043A\\u0442  \n\nItem_\\# = \\u041F\\u0443\\u043D\\u043A\\u0442 \\u2116\n\nLabor_Day = Labor Day\n\nLabour_Day_(Can) = Labour Day (Can)\n\nLast = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0439\n\nLast_Name\\: = \\u0424\\u0430\\u043C\\u0438\\u043B\\u0438\\u044F:\n\nLicense = \\u041B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u044F\n\nLook_and_Feel\\: = \\u0422\\u0435\\u043C\\u0430 \\u0418\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430:\n\nMartin_Luther_King_Day = Martin Luther King Day\n\nMemorial_Day = Memorial Day\n\nMonth_View = \\u041F\\u0440\\u043E\\u0441\\u043C\\u043E\\u0442\\u0440 \\u041C\\u0435\\u0441\\u044F\\u0446\\u0430\n\nMother's_Day = Mother's Day\n\nNEW_Item = \\u041D\\u041E\\u0412\\u042B\\u0419 \\u041F\\u0443\\u043D\\u043A\\u0442 \n\nNew_State = \\u041D\\u043E\\u0432\\u043E\\u0435 \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nNew_Task_Type = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0422\\u0438\\u043F \\u0417\\u0430\\u0434\\u0430\\u0447\\u0438\n\nNew_Year's_Day = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0413\\u043E\\u0434\n\nNext_States = \\u0421\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0438\\u0435 \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u044F\n\nNickname\\: = \\u041D\\u0438\\u043A:\n\nNo_Specific_Time = \\u0411\\u0435\\u0437 \\u0423\\u043A\\u0430\\u0437\\u0430\\u043D\\u0438\\u044F \\u0412\\u0440\\u0435\\u043C\\u0435\\u043D\\u0438\n\nNotes = \\u0417\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438\n\nNotice = \\u0423\\u0432\\u0435\\u0434\\u043E\\u043C\\u043B\\u0435\\u043D\\u0438\\u0435\n\nOK = \\u0414\\u0410\n\nOpen = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C\n\nOpen_Calendar = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C \\u041A\\u0430\\u043B\\u0435\\u043D\\u0434\\u0430\\u0440\\u044C\n\nOptions = \\u041E\\u043F\\u0446\\u0438\\u0438\n\nPA = \\u041F\\u0410\n\nPager\\: = \\u041F\\u0435\\u0439\\u0434\\u0436\\u0435\\u0440:\n\nPassword = \\u041F\\u0430\\u0440\\u043E\\u043B\\u044C\n\nPlease_choose_File_to_Import_From = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435, \\u0438\\u0437 \\u043A\\u0430\\u043A\\u043E\\u0433\\u043E \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435\n\nPlease_choose_directory_to_place_XML_files = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u043A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433, \\u0432 \\u043A\\u043E\\u0442\\u043E\\u0440\\u044B\\u0439 \\u0441\\u043B\\u0435\\u0434\\u0443\\u0435\\u0442 \\u043F\\u043E\\u043C\\u0435\\u0441\\u0442\\u0438\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B\\u044B XML \n\nPlease_enter_some_appointment_text = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430, \\u0432\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u043E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nPlease_select_a_state = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430 \\u0443\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0441\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nPlease_select_a_type = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430 \\u0443\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0442\\u0438\\u043F\n\nPresidents_Day = Presidents Day\n\nPri = \\u041F\\u0440\\u0438\n\nPrint = \\u041F\\u0435\\u0447\\u0430\\u0442\\u0430\\u0442\\u044C\n\nPrint_Chooser = \\u0412\\u044B\\u0431\\u043E\\u0440 \\u041F\\u0440\\u0438\\u043D\\u0442\\u0435\\u0440\\u0430\n\nPrint_In_Color? = \\u041F\\u0435\\u0447\\u0430\\u0442\\u044C \\u0432 \\u0426\\u0432\\u0435\\u0442\\u0435?\n\nPrivate = \\u0427\\u0430\\u0441\\u0442\\u043D\\u043E\\u0435\n\nProperties = \\u0421\\u0432\\u043E\\u0439\\u0441\\u0442\\u0432\\u0430\n\nReally_Delete_ = \\u0412 \\u0441\\u0430\\u043C\\u043E\\u043C \\u0434\\u0435\\u043B\\u0435 \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \n\nReally_change_the_database? = \\u0414\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u0442\\u0435\\u043B\\u044C\\u043D\\u043E \\u0438\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0411\\u0414?\n\nReally_delete_number_ = \\u0414\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u0442\\u0435\\u043B\\u044C\\u043D\\u043E \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u043D\\u043E\\u043C\\u0435\\u0440 \n\nRecurrence = \\u041F\\u043E\\u0432\\u0442\\u043E\\u0440\\u0435\\u043D\\u0438\\u0435\n\nRemembrance_Day = Remembrance Day\n\nRename_State = \\u041F\\u0435\\u0440\\u0435\\u0438\\u043C\\u0435\\u043D\\u043E\\u0432\\u0430\\u0442\\u044C \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nRename_Type = \\u041F\\u0435\\u0440\\u0435\\u0438\\u043C\\u0435\\u043D\\u043E\\u0432\\u0430\\u0442\\u044C \\u0422\\u0438\\u043F\n\nReset_Task_States_to_Default = \\u0421\\u0431\\u0440\\u043E\\u0441\\u0438\\u0442\\u044C \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u044F \\u0417\\u0430\\u0434\\u0430\\u0447 \\u0432 \\u0423\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044F\n\nResolution = \\u0420\\u0435\\u0437\\u043E\\u043B\\u044E\\u0446\\u0438\\u044F\n\nSMTP_Server = SMTP \\u0421\\u0435\\u0440\\u0432\\u0435\\u0440\n\nSave = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C\n\nScreen_Name\\: = \\u041F\\u043E\\u043A\\u0430\\u0437\\u044B\\u0432\\u0430\\u0442\\u044C \\u043A\\u0430\\u043A:\n\nSearch_Results = \\u041D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E\n\nSelect = \\u0412\\u044B\\u0431\\u0440\\u0430\\u0442\\u044C\n\nSelect_next_state = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0441\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0435\\u0435 \\u0441\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435\n\nShow_Canadian_Holidays = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u041A\\u0430\\u043D\\u0430\\u0434\\u0441\\u043A\\u0438\\u0435 \\u041F\\u0440\\u0430\\u0437\\u0434\\u043D\\u0438\\u043A\\u0438\n\nShow_Private_Appointments = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0427\\u0430\\u0441\\u0442\\u043D\\u044B\\u0435 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nShow_Public_Appointments = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u041F\\u0443\\u0431\\u043B\\u0438\\u0447\\u043D\\u044B\\u0435 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nShow_Stack_Trace = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0421\\u0442\\u0435\\u043A \\u0412\\u044B\\u0437\\u043E\\u0432\\u043E\\u0432\n\nShow_U.S._Holidays = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0410\\u043C\\u0435\\u0440\\u0438\\u043A\\u0430\\u043D\\u0441\\u043A\\u0438\\u0435 \\u041F\\u0440\\u0430\\u0437\\u0434\\u043D\\u0438\\u043A\\u0438\n\nSt._Patrick's_Day = \\u0414\\u0435\\u043D\\u044C \\u0441\\u0432.\\u041F\\u0430\\u0442\\u0440\\u0438\\u043A\\u0430\n\nStandard_Time = Standard Time\n\nStartDate = \\u0421\\u0442\\u0430\\u0440\\u0442\n\nStart_Date = \\u0421\\u0442\\u0430\\u0440\\u0442\n\nStart_Time\\: = \\u0421\\u0442\\u0430\\u0440\\u0442:\n\nStates = \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u044F\n\nStatus = \\u0421\\u0442\\u0430\\u0442\\u0443\\u0441\n\nSubTasks = \\u041F\\u043E\\u0434\\u0417\\u0430\\u0434\\u0430\\u0447\\u0438\n\nTaskInformation = \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F \\u043E \\u0417\\u0430\\u0434\\u0430\\u0447\\u0435\n\nTask_State_Editor = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u043E\\u0440 \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0439 \\u0417\\u0430\\u0434\\u0430\\u0447\n\nTask_Types = \\u0422\\u0438\\u043F\\u044B \\u0417\\u0430\\u0434\\u0430\\u0447\n\nText = \\u0422\\u0435\\u043A\\u0441\\u0442\n\nThanksgiving = Thanksgiving\n\nThanksgiving_(Can) = Thanksgiving (Can)\n\nTime = \\u0412\\u0440\\u0435\\u043C\\u044F\n\nTimes = \\u0420\\u0430\\u0437\n\nTo_Do = \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C\n\nTo_Do_List = \\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0414\\u0435\\u043B\n\nToday = \\u0421\\u0435\\u0433\\u043E\\u0434\\u043D\\u044F\n\nType = \\u0422\\u0438\\u043F\n\nUse_24_hour_time_format = \\u0414\\u0432\\u0430\\u0434\\u0446\\u0430\\u0442\\u0438\\u0447\\u0435\\u0442\\u044B\\u0440\\u0435\\u0445\\u0447\\u0430\\u0441\\u043E\\u0432\\u043E\\u0439 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442\n\nVacation = \\u041A\\u0430\\u043D\\u0438\\u043A\\u0443\\u043B\\u044B\n\nValentine's_Day = \\u0414\\u0435\\u043D\\u044C \\u0441\\u0432.\\u0412\\u0430\\u043B\\u0435\\u043D\\u0442\\u0438\\u043D\\u0430\n\nVeteran's_Day = Veteran's Day\n\nVictoria_Day = Victoria Day\n\nWeb_Page\\: = \\u0412\\u0435\\u0431\\u0441\\u0430\\u0439\\u0442:\n\nWeek_Starts_with_Monday = \\u041D\\u0435\\u0434\\u0435\\u043B\\u044F \\u0441 \\u041F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u0438\\u043A\\u0430\n\nWeek_View = \\u041F\\u0440\\u043E\\u0441\\u043C\\u043E\\u0442\\u0440 \\u041D\\u0435\\u0434\\u0435\\u043B\\u0438\n\nWeek_View_End_Hour\\:_ = \\u041A\\u043E\\u043D\\u0435\\u0446 \\u0440\\u0430\\u0431\\u043E\\u0447\\u0435\\u0433\\u043E \\u0434\\u043D\\u044F: \n\nWeek_View_Start_Hour\\:_ = \\u041D\\u0430\\u0447\\u0430\\u043B\\u043E \\u0440\\u0430\\u0431\\u043E\\u0447\\u0435\\u0433\\u043E \\u0434\\u043D\\u044F: \n\nWorkAddress = \\u0420\\u0430\\u0431\\u043E\\u0447\\u0438\\u0439 \\u0410\\u0434\\u0440\\u0435\\u0441\n\nWork_City\\: = \\u0413\\u043E\\u0440\\u043E\\u0434:\n\nWork_Country\\: = \\u0421\\u0442\\u0440\\u0430\\u043D\\u0430:\n\nWork_Phone = \\u0420\\u0430\\u0431\\u043E\\u0447\\u0438\\u0439 \\u0442\\u0435\\u043B.\n\nWork_Phone\\: = \\u0420\\u0430\\u0431\\u043E\\u0447\\u0438\\u0439 \\u0442\\u0435\\u043B.:\n\nWork_Street_Address = \\u0410\\u0434\\u0440\\u0435\\u0441\n\nWork_Zip_Code\\: = \\u0418\\u043D\\u0434\\u0435\\u043A\\u0441:\n\nYour_Email_Address = \\u0412\\u0430\\u0448 \\u041F\\u043E\\u0447\\u0442\\u043E\\u0432\\u044B\\u0439 \\u0410\\u0434\\u0440\\u0435\\u0441\n\n]_does_not_exist = ] \\u043D\\u0435 \\u0441\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442\n\n]_is_not_a_directory = ] \\u043D\\u0435 \\u044F\\u0432\\u043B\\u044F\\u0435\\u0442\\u0441\\u044F \\u043A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433\\u043E\\u043C\n\n__through__ = \\u0432 \\u0442\\u0435\\u0447\\u0435\\u043D\\u0438\\u0435\n\naddcat = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u041D\\u043E\\u0432\\u0443\\u044E \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u044E\n\nappearance = \\u0412\\u043D\\u0435\\u0448\\u043D\\u0438\\u0439 \\u0412\\u0438\\u0434\n\napply = \\u041F\\u0440\\u0438\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\n\nappt_error = \\u041E\\u043A\\u043D\\u043E \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F \\u0431\\u0443\\u0434\\u0435\\u0442 \\u0437\\u0430\\u043A\\u0440\\u044B\\u0442\\u043E \\u0432\\u0441\\u043B\\u0435\\u0434\\u0441\\u0442\\u0432\\u0438\\u0435 \\u043E\\u0431\\u043D\\u0430\\u0440\\u0443\\u0436\\u0435\\u043D\\u043D\\u043E\\u0439 \\u043E\\u0448\\u0438\\u0431\\u043A\\u0438. \\u0412\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E, \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u0435 \\u0431\\u044B\\u043B\\u043E \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\\u043E \\u0438\\u0437 \\u043E\\u043A\\u043D\\u0430 \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C \\u043A\\u043E\\u0433\\u0434\\u0430 \\u0440\\u0435\\u0434\\u0430\\u043A\\u0442\\u043E\\u0440 \\u0431\\u044B\\u043B \\u043E\\u0442\\u043A\\u0440\\u044B\\u0442. \\u0415\\u0441\\u043B\\u0438 \\u043D\\u0435\\u0442, The \\u043F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0443\\u0441\\u0442\\u0438\\u0442\\u0435 Borg\n\napptlist = \\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u0439\n\nappttext = \\u0422\\u0435\\u043A\\u0441\\u0442 \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nappttime = \\u0412\\u0440\\u0435\\u043C\\u044F \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nbad_db_2 = \\\\n\\u0415\\u0441\\u043B\\u0438 \\u043E\\u0448\\u0438\\u0431\\u043A\\u0430 \\u0432\\u044B\\u0437\\u0432\\u0430\\u043D\\u0430 \\u043D\\u0435\\u0432\\u0435\\u0440\\u043D\\u043E \\u0443\\u043A\\u0430\\u0437\\u0430\\u043D\\u043D\\u044B\\u043C \\u043A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433\\u043E\\u043C \\u0411\\u0414, \\u043E\\u0442\\u0432\\u0435\\u0442\\u044C\\u0442\\u0435 \\u0414\\u0410 \\u0447\\u0442\\u043E\\u0431\\u044B \\u0438\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0435\\u0433\\u043E.\n\nbiweekly = \\u0440\\u0430\\u0437 \\u0432 \\u0434\\u0432\\u0435 \\u043D\\u0435\\u0434\\u0435\\u043B\\u0438\n\nblack = \\u0427\\u0435\\u0440\\u043D\\u044B\\u0439\n\nblue = \\u0421\\u0438\\u043D\\u0438\\u0439\n\ncatchooser = \\u0412\\u044B\\u0431\\u043E\\u0440 \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u0438\n\nchangedate = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0414\\u0430\\u0442\\u0443\n\nchoose_file = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0424\\u0430\\u0439\\u043B\n\nchoosecat = \\u041F\\u043E\\u043A\\u0430\\u0437\\u044B\\u0432\\u0430\\u0442\\u044C \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u0438...\n\nclear_all = \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C \\u0412\\u0441\\u0435\n\ncontact = \\u041A\\u043E\\u043D\\u0442\\u0430\\u043A\\u0442\\u043D\\u0430\\u044F \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F\n\ndaily = \\u0435\\u0436\\u0435\\u0434\\u043D\\u0435\\u0432\\u043D\\u043E\n\ndb_set_to = \\\\n\\\\n\\u0412\\u0430\\u0448 \\u043A\\u0430\\u0442\\u0430\\u043B\\u043E\\u0433 \\u0411\\u0414 \\u0443\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D \\u0432: \n\ndel_tip = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0441\\u0435 \\u0432\\u0445\\u043E\\u0436\\u0434\\u0435\\u043D\\u0438\\u044F (\\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u0435\\u043D\\u0438\\u044F) \\u0434\\u0430\\u043D\\u043D\\u043E\\u0433\\u043E \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\ndelete_selected = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0412\\u044B\\u0434\\u0435\\u043B\\u0435\\u043D\\u043D\\u043E\\u0435\n\ndoo_tip = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0442\\u043E\\u043B\\u044C\\u043A\\u043E \\u0442\\u0435\\u043A\\u0443\\u0449\\u0435\\u0435 \\u0432\\u0445\\u043E\\u0436\\u0434\\u0435\\u043D\\u0438\\u0435 \\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044F\\u044E\\u0449\\u0435\\u0433\\u043E\\u0441\\u044F \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nedit_types = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0422\\u0438\\u043F\\u044B \\u0438 \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u044F \\u0417\\u0430\\u0434\\u0430\\u0447\n\nenable_popups = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F \\u043F\\u043E \\u043F\\u043E\\u0447\\u0442\\u0435\\n\n\nenturl = \\u0412\\u0432\\u0435\\u0441\\u0442\\u0438 URL:\n\nep = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438\n\nexpXML = \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C XML...\n\nforever = \\u0411\\u0435\\u0441\\u043A\\u043E\\u043D\\u0435\\u0447\\u043D\\u043E\n\ngreen = \\u0417\\u0435\\u043B\\u0435\\u043D\\u044B\\u0439\n\nimpXML = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C XML...\n\nimpexpMenu = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 / \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\n\nlocale = \\u041B\\u043E\\u043A\\u0430\\u043B\\u044C (\\u0442\\u0440\\u0435\\u0431\\u0443\\u0435\\u0442\\u0441\\u044F \\u043F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0443\\u0441\\u043A \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u044B)\n\nmin_aft_app = \\u043C\\u0438\\u043D\\u0443\\u0442 \\u043F\\u043E\\u0441\\u043B\\u0435 \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nmin_bef_app = \\u043C\\u0438\\u043D\\u0443\\u0442 \\u0434\\u043E \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u044F\n\nmisc = \\u0420\\u0430\\u0437\\u043D\\u043E\\u0435\n\nmonthly = \\u0435\\u0436\\u0435\\u043C\\u0435\\u0441\\u044F\\u0447\\u043D\\u043E (\\u0434\\u0430\\u0442\\u0430)\n\nmonthly_day = \\u0435\\u0436\\u0435\\u043C\\u0435\\u0441\\u044F\\u0447\\u043D\\u043E (\\u0434\\u0435\\u043D\\u044C)\n\nmwf = \\u041F\\u043D-\\u0421\\u0440-\\u041F\\u0442\n\nnavy = \\u0412\\u041C\\u0424\n\nndays = \\u041A\\u0430\\u0436\\u0434\\u044B\\u0435 N \\u0414\\u043D\\u0435\\u0439\n\nnewDate\\: = \\u041D\\u043E\\u0432\\u0430\\u044F \\u0414\\u0430\\u0442\\u0430:\n\nnummonths = \\u0421\\u043A\\u043E\\u043B\\u044C\\u043A\\u043E \\u041C\\u0435\\u0441\\u044F\\u0446\\u0435\\u0432 \\u041F\\u0435\\u0447\\u0430\\u0442\\u0430\\u0442\\u044C?\n\nonce = \\u043E\\u0434\\u043D\\u0430\\u0436\\u0434\\u044B\n\npopup_reminders = \\u041F\\u043E\\u0447\\u0442\\u043E\\u0432\\u044B\\u0435 \\u0423\\u0432\\u0435\\u0434\\u043E\\u043C\\u043B\\u0435\\u043D\\u0438\\u044F\n\nred = \\u041A\\u0440\\u0430\\u0441\\u043D\\u044B\\u0439\n\nremcat = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u041D\\u0435\\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u0443\\u0435\\u043C\\u044B\\u0435 \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u0438\n\nreset_state_warning = \\u042D\\u0442\\u043E \\u0441\\u0431\\u0440\\u043E\\u0441\\u0438\\u0442 \\u0432\\u0441\\u0435 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u044F \\u043A\\u043E\\u0442\\u043E\\u0440\\u044B\\u0435 \\u0412\\u044B \\u0441\\u0434\\u0435\\u043B\\u0430\\u043B\\u0438 \\u0432 \\u043C\\u043E\\u0434\\u0435\\u043B\\u0438 \\u0437\\u0430\\u0434\\u0430\\u0447.\\\\n\\u041C\\u043E\\u0434\\u0435\\u043B\\u044C \\u0437\\u0430\\u0434\\u0430\\u0447 \\u0431\\u0443\\u0434\\u0435\\u0442 \\u0441\\u0431\\u0440\\u043E\\u0448\\u0435\\u043D\\u0430 \\u0432 \\u0441\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435, \\u0437\\u0430\\u0444\\u0438\\u043A\\u0441\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u043D\\u043E\\u0435 \\u0432 \\u043F\\u043E\\u0441\\u0442\\u0430\\u0432\\u043A\\u0435.\\\\n\\u0412\\u044B \\u043F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0436\\u0434\\u0430\\u0435\\u0442\\u0435 \\u0432\\u044B\\u0431\\u043E\\u0440?\\\\n\n\nsave_Def = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0423\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044F\n\nsd_tip = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0442\\u0435\\u043A\\u0443\\u0449\\u0438\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u044F \\u0432 \\u043A\\u0430\\u0447\\u0435\\u0441\\u0442\\u0432\\u0435 \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u0439 \\u0434\\u043B\\u044F \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u0439\n\nselect_all = \\u0412\\u044B\\u0431\\u0440\\u0430\\u0442\\u044C \\u0412\\u0441\\u0435\n\nselectdb = \\u0411\\u0414 \\u043D\\u0435 \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0435\\u043D\\u0430. \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430, \\u0432\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u0411\\u0414.\n\nset_def_font = \\u0428\\u0440\\u0438\\u0444\\u0442 \\u041F\\u043E \\u0423\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\n\nshowdoy = \\u0412\\u044B\\u0432\\u0435\\u0441\\u0442\\u0438 \\u041D\\u043E\\u043C\\u0435\\u0440 \\u0414\\u043D\\u044F\n\nsplash = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0437\\u0430\\u0441\\u0442\\u0430\\u0432\\u043A\\u0443\n\nsrch = \\u041F\\u043E\\u0438\\u0441\\u043A\n\nstackonerr = \\u041E\\u043F\\u0446\\u0438\\u044F \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430 \\u0421\\u0442\\u0435\\u043A\\u0430 \\u0412\\u044B\\u0437\\u043E\\u0432\\u043E\\u0432 \\u0432 \\u0414\\u0438\\u0430\\u0434\\u043B\\u043E\\u0433\\u0430\\u0445 \\u041E\\u0448\\u0438\\u0431\\u043A\\u0438 \n\ntodomissingdata = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430 \\u0432\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u0442\\u0435\\u043A\\u0441\\u0442 \\u0438 \\u0434\\u0430\\u0442\\u0443 \\u043F\\u043E\\u0440\\u0443\\u0447\\u0435\\u043D\\u0438\\u044F.\n\ntodoquickentry = \\u0411\\u044B\\u0441\\u0442\\u0440\\u043E \\u0432\\u043D\\u0435\\u0441\\u0442\\u0438 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435\n\ntth = \\u0412\\u0442-\\u0427\\u0442\n\nuncategorized = <\\u0412\\u043D\\u0435 \\u041A\\u0430\\u0442\\u0435\\u0433\\u043E\\u0440\\u0438\\u0439>\n\nviewchglog = \\u0421\\u043C\\u043E\\u0442\\u0440\\u0435\\u0442\\u044C \\u041B\\u043E\\u0433 \\u0418\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u0439\n\nweekdays = \\u041F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u0438\\u043A-\\u041F\\u044F\\u0442\\u043D\\u0438\\u0446\\u0430\n\nweekends = \\u0421\\u0443\\u0431\\u0431\\u043E\\u0442\\u0430 \\u0438 \\u0412\\u043E\\u0441\\u043A\\u0440\\u0435\\u0441\\u0435\\u043D\\u044C\\u0435\n\nweekly = \\u0435\\u0436\\u0435\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u043E\n\nwhite = \\u0411\\u0435\\u043B\\u044B\\u0439\n\nyearly = \\u0435\\u0436\\u0435\\u0433\\u043E\\u0434\\u043D\\u043E\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/borg_resource_zh.properties",
    "content": "\n*****_NEW_APPT_***** = ***** \\u65B0\\u5EFA\\u7EA6\\u4F1A *****\n\n-db_argument_is_missing = -db \\u53C2\\u6570\\u7F3A\\u5931\n\nAbout = \\u5173\\u4E8E\n\nAbout_BORG = \\u5173\\u4E8EBORG\n\nAction = \\u64CD\\u4F5C\n\nAdd = \\u6DFB\\u52A0\n\nAddCat = \\u8F93\\u5165\\u65B0\\u7684\\u7C7B\\u522B\\u540D\\u79F0:\n\nAdd_New = \\u6DFB\\u52A0\n\nAdd_State = \\u72B6\\u6001\\u6DFB\\u52A0\n\nAdd_Type = \\u6DFB\\u52A0\\u7C7B\\u578B\n\nAddress = \\u5730\\u5740\\u4FE1\\u606F\n\nAddress_Book = \\u5730\\u5740\\u672C\n\nAddress_Book_Entry = \\u5730\\u5740\\u672C\\u9879\\u76EE\n\nAll = \\u5168\\u90E8\n\nApply_DB_Change = \\u5E94\\u7528DB\\u4FEE\\u6539\n\nAppointment_Editor = \\u7EA6\\u4F1A\\u7F16\\u8F91\\u5668\n\nAppointment_Editor_for_ = \\u7EA6\\u4F1A\\u7F16\\u8F91\\u5668 -  \n\nBerger-Organizer_v = Berger-Organizer v\n\nBirthday = \\u751F\\u65E5\n\nBoxing_Day = \\u8282\\u793C\\u65E5\n\nBrowse = \\u6D4F\\u89C8\n\nBuild_Time\\:_ = \\\\nBuild Time:\\\\n\n\nCLOSED = CLOSED\n\nCanada_Day = \\u52A0\\u62FF\\u5927\\u56FD\\u5E86\n\nCancel = \\u53D6\\u6D88\n\nCategories = \\u7C7B\\u522B\n\nCategory = \\u7C7B\\u522B\n\nCell_Phone\\: = \\u624B\\u673A:\n\nChange = \\u7F16\\u8F91\n\nChristmas = \\u5723\\u8BDE\\u8282\n\nCivic_Holiday = \\u516C\\u4F11\\u65E5\n\nClone = \\u514B\\u9686\n\nClose = \\u6807\\u6CE8\\u5B8C\\u6210\n\nClosed = \\u5DF2\\u5B8C\\u6210\\u7684\n\nColor = \\u989C\\u8272\n\nColumbus_Day = \\u54E5\\u4F26\\u5E03\\u53D1\\u73B0\\u7F8E\\u6D32\\u7EAA\\u5FF5\\u65E5\n\nCommonwealth_Day = \\u82F1\\u8054\\u90A6\\u8282\n\nCompany = \\u516C\\u53F8\n\nConfirm_DB_Change = \\u786E\\u8BA4DB\\u4FEE\\u6539\n\nConfirm_Delete = \\u786E\\u8BA4\\u5220\\u9664\n\nCould_not_parse_times\\:_ = \\u65E0\\u6CD5\\u8BFB\\u53D6\\u65F6\\u95F4\\u683C\\u5F0F: \n\nDataBase_Directory = \\u6570\\u636E\\u5E93\\u76EE\\u5F55\n\nDatabaseInformation = \\u6570\\u636E\\u5E93\n\nDate = \\u65E5\\u671F\n\nDay_View = \\u6309\\u65E5\\u67E5\\u770B\n\nDaylight_Savings_Time = \\u590F\\u65F6\\u5236\\u65F6\\u95F4\n\nDays_Left = \\u5269\\u4F59\\u5929\\u6570\n\nDelete = \\u5220\\u9664\n\nDelete_One_Only = \\u4EC5\\u5220\\u9664\\u4E00\\u4E2A\\u7EA6\\u4F1A\n\nDelete_State = \\u72B6\\u6001\\u5220\\u9664\n\nDelete_Type = \\u5220\\u9664\\u7C7B\\u578B\n\nDescription = \\u63CF\\u8FF0\n\nDirectory_[ = \\u76EE\\u5F55 [\n\nDiscard_Text? = \\u653E\\u5F03\\u7EA6\\u4F1A\\u5185\\u5BB9\\u5417?\n\nDismiss = \\u53D6\\u6D88\n\nDone_(Delete) = \\u5DF2\\u5B8C\\u6210(\\u5220\\u9664)\n\nDone_(No_Delete) = \\u5DF2\\u5B8C\\u6210(\\u4E0D\\u5220\\u9664)\n\nDueDate = \\u7ED3\\u675F\\u65E5\\u671F\n\nDue_Date = \\u7ED3\\u675F\\u65E5\\u671F\n\nEdit = \\u7F16\\u8F91\n\nEditNew = \\u65B0\\u5EFA\n\nEmail = Email\n\nEmail\\: = Email:\n\nEmailParameters = \\u7535\\u5B50\\u90AE\\u4EF6\n\nEnable_Email = \\u5141\\u8BB8\\u4F7F\\u7528\\u7535\\u5B50\\u90AE\\u4EF6\n\nExit = \\u9000\\u51FA\n\nFather's_Day = \\u7236\\u4EB2\\u8282\n\nFax\\: = \\u4F20\\u771F:\n\nFilter\\: = \\u7B5B\\u9009:\n\nFirst = \\u540D(First)\n\nFirst_Name\\: = \\u540D(First Name):\n\nFrequency = \\u91CD\\u590D\\u9891\\u7387\n\nGo_To = \\u8DF3\\u81F3\\u6307\\u5B9A\\u6708\n\nGround_Hog_Day = \\u571F\\u62E8\\u9F20\\u65E5\n\nHalf_Day = \\u534A\\u5929\n\nHalloween = \\u4E07\\u5723\\u8282\n\nHelp = \\u5E2E\\u52A9\n\nHoliday = \\u8282\\u65E5\n\nHomeAddress = \\u4F4F\\u5B85\\u5730\\u5740\n\nHome_City\\: = \\u57CE\\u5E02:\n\nHome_Country\\: = \\u56FD\\u5BB6:\n\nHome_Phone = \\u4F4F\\u5B85\\u7535\\u8BDD\n\nHome_Phone\\: = \\u4F4F\\u5B85\\u7535\\u8BDD:\n\nHome_State\\: = \\u7701/\\u5E02/\\u533A:\n\nHome_Street_Address = \\u8857\\u9053\n\nHome_Zip_Code\\: = \\u90AE\\u653F\\u7F16\\u7801:\n\nImport = \\u5BFC\\u5165\n\nImport_WARNING = \\u5BFC\\u5165\\u8B66\\u544A\n\nImporting_ = \\u6B63\\u5728\\u5BFC\\u5165\n\nIndependence_Day = \\u7F8E\\u56FD\\u72EC\\u7ACB\\u7EAA\\u5FF5\\u65E5\n\nInitializing = \\u521D\\u59CB\\u5316\n\nItem = \\u9879\n\nItem_ = \\u9879 \n\nItem_\\# = \\u5E8F\\u53F7\n\nLabor_Day = \\u7F8E\\u56FD\\u52B3\\u52A8\\u65E5\n\nLabour_Day_(Can) = \\u52A0\\u62FF\\u5927\\u52B3\\u52A8\\u65E5\n\nLast = \\u59D3(Last)\n\nLast_Name\\: = \\u59D3(Last Name):\n\nLicense = \\u7528\\u6237\\u8BB8\\u53EF\\u534F\\u8BAE\n\nLook_and_Feel\\: = \\u7A0B\\u5E8F\\u754C\\u9762\\u5916\\u89C2:\n\nMartin_Luther_King_Day = \\u9A6C\\u4E01\\u8DEF\\u5FB7\\u91D1\\u7EAA\\u5FF5\\u65E5\n\nMemorial_Day = \\u7F8E\\u56FD\\u9635\\u4EA1\\u6218\\u58EB\\u7EAA\\u5FF5\\u65E5\n\nMonth_View = \\u6309\\u6708\\u67E5\\u770B\n\nMother's_Day = \\u6BCD\\u4EB2\\u8282\n\nNEW_Item = \\u65B0\\u4EFB\\u52A1\n\nNew_State = \\u65B0\\u72B6\\u6001\n\nNew_Task_Type = \\u65B0\\u7684\\u4EFB\\u52A1\\u7C7B\\u578B\n\nNew_Year's_Day = \\u5143\\u65E6\n\nNext_States = \\u540E\\u7EED\\u72B6\\u6001\n\nNickname\\: = \\u6635\\u79F0:\n\nNo_Specific_Time = \\u4E0D\\u6307\\u5B9A\\u65F6\\u95F4\n\nNotes = \\u5907\\u6CE8\n\nNotice = Notice\n\nOK = \\u786E\\u5B9A\n\nOpen = \\u672A\\u6267\\u884C\\u7684\n\nOpen_Calendar = \\u6253\\u5F00\\u65E5\\u5386\n\nOptions = \\u9009\\u9879\n\nPA = \\u4E2A\\u4EBA\\u6307\\u5B9A\n\nPager\\: = \\u79FB\\u52A8\\u7535\\u8BDD:\n\nPassword = \\u5BC6\\u7801\n\nPlease_choose_File_to_Import_From = \\u8BF7\\u9009\\u62E9\\u8981\\u5BFC\\u5165\\u7684\\u6587\\u4EF6\n\nPlease_choose_directory_to_place_XML_files = \\u8BF7\\u9009\\u62E9XML\\u6587\\u4EF6\\u6240\\u5728\\u7684\\u76EE\\u5F55\n\nPlease_enter_some_appointment_text = \\u8BF7\\u8F93\\u5165\\u7EA6\\u4F1A\\u7684\\u5185\\u5BB9\n\nPlease_select_a_state = \\u8BF7\\u5148\\u9009\\u62E9\\u4E00\\u79CD\\u72B6\\u6001\n\nPlease_select_a_type = \\u8BF7\\u5148\\u9009\\u62E9\\u4E00\\u4E2A\\u7C7B\\u578B\n\nPresidents_Day = \\u7F8E\\u56FD\\u603B\\u7EDF\\u65E5\n\nPri = \\u4F18\\u5148\\u7EA7\n\nPrint = \\u6253\\u5370\n\nPrint_Chooser = \\u6253\\u5370\\u9009\\u62E9\n\nPrint_In_Color? = \\u4F7F\\u7528\\u5F69\\u8272\\u6253\\u5370?\n\nPrivate = \\u79C1\\u5BC6\\u7684\n\nProperties = \\u5C5E\\u6027\n\nReally_Delete_ = \\u5F7B\\u5E95\\u5220\\u9664 \n\nReally_change_the_database? = \\u786E\\u5B9E\\u8981\\u66F4\\u6539\\u6570\\u636E\\u5E93\\u5417?\n\nReally_delete_number_ = \\u5F7B\\u5E95\\u5220\\u9664\\u7684\\u5E8F\\u53F7 \n\nRecurrence = \\u91CD\\u590D\n\nRemembrance_Day = \\u8363\\u519B\\u65E5\n\nRename_State = \\u72B6\\u6001\\u91CD\\u547D\\u540D\n\nRename_Type = \\u91CD\\u547D\\u540D\\u7C7B\\u578B\n\nReset_Task_States_to_Default = \\u5C06\\u4EFB\\u52A1\\u8BBE\\u4E3A\\u9ED8\\u8BA4\\u72B6\\u6001\n\nResolution = \\u8FDB\\u5C55\\u60C5\\u51B5\n\nSMTP_Server = SMTP\\u670D\\u52A1\\u5668\n\nSave = \\u4FDD\\u5B58\n\nScreen_Name\\: = \\u663E\\u793A\\u540D\\u79F0:\n\nSearch_Results = \\u641C\\u7D22\\u7ED3\\u679C\n\nSelect = \\u9009\\u62E9\n\nSelect_next_state = \\u9009\\u62E9\\u540E\\u7EED\\u72B6\\u6001\n\nShow_Canadian_Holidays = \\u663E\\u793A\\u52A0\\u62FF\\u5927\\u8282\\u65E5\n\nShow_Private_Appointments = \\u663E\\u793A\\u79C1\\u5BC6\\u7684\\u7EA6\\u4F1A\\u4FE1\\u606F\n\nShow_Public_Appointments = \\u663E\\u793A\\u516C\\u5F00\\u7684\\u7EA6\\u4F1A\\u4FE1\\u606F\n\nShow_Stack_Trace = \\u663E\\u793A\\u5806\\u6808\\u8DDF\\u8E2A\\u4FE1\\u606F\n\nShow_U.S._Holidays = \\u663E\\u793A\\u7F8E\\u56FD\\u8282\\u65E5\n\nSt._Patrick's_Day = \\u5723\\u5E15\\u7279\\u91CC\\u514B\\u8282\n\nStandard_Time = \\u6807\\u51C6\\u65F6\\u95F4\n\nStartDate = \\u5F00\\u59CB\\u65E5\\u671F\n\nStart_Date = \\u5F00\\u59CB\\u65E5\\u671F\n\nStart_Time\\: = \\u5F00\\u59CB\\u65F6\\u95F4:\n\nStates = \\u72B6\\u6001\n\nStatus = \\u72B6\\u6001\n\nSubTasks = \\u5B50\\u4EFB\\u52A1\n\nTaskInformation = \\u4EFB\\u52A1\\u4FE1\\u606F\n\nTask_State_Editor = \\u7F16\\u8F91\\u4EFB\\u52A1\\u7684\\u72B6\\u6001\n\nTask_Types = \\u4EFB\\u52A1\\u7C7B\\u578B\n\nText = \\u5185\\u5BB9\n\nThanksgiving = \\u611F\\u6069\\u8282\n\nThanksgiving_(Can) = \\u611F\\u6069\\u8282(\\u52A0\\u62FF\\u5927)\n\nTime = \\u65F6\\u95F4\n\nTimes = \\u91CD\\u590D\\u6B21\\u6570\n\nTo_Do = \\u5F85\\u529E\\u4E8B\\u9879\n\nTo_Do_List = \\u5F85\\u529E\\u4E8B\\u9879\\u5217\\u8868\n\nToday = \\u8DF3\\u5230\\u4ECA\\u65E5\n\nType = \\u7C7B\\u578B\n\nUse_24_hour_time_format = \\u4F7F\\u752824\\u5C0F\\u65F6\\u683C\\u5F0F\n\nVacation = \\u5EA6\\u5047\n\nValentine's_Day = \\u60C5\\u4EBA\\u8282\n\nVeteran's_Day = \\u7F8E\\u56FD\\u8001\\u5175\\u7EAA\\u5FF5\\u65E5\n\nVictoria_Day = \\u7EF4\\u591A\\u5229\\u4E9A\\u7EAA\\u5FF5\\u65E5\n\nWeb_Page\\: = \\u4E3B\\u9875:\n\nWeek_Starts_with_Monday = \\u6BCF\\u5468\\u5F00\\u59CB\\u4E8E\\u661F\\u671F\\u4E00\n\nWeek_View = \\u6309\\u5468\\u67E5\\u770B\n\nWeek_View_End_Hour\\:_ = \\u6BCF\\u65E5\\u5DE5\\u4F5C\\u65F6\\u95F4\\u7ED3\\u675F\\u4E8E: \n\nWeek_View_Start_Hour\\:_ = \\u6BCF\\u65E5\\u5DE5\\u4F5C\\u65F6\\u95F4\\u5F00\\u59CB\\u4E8E: \n\nWorkAddress = \\u529E\\u516C\\u5730\\u5740\n\nWork_City\\: = \\u57CE\\u5E02:\n\nWork_Country\\: = \\u56FD\\u5BB6:\n\nWork_Phone = \\u529E\\u516C\\u7535\\u8BDD\n\nWork_Phone\\: = \\u529E\\u516C\\u7535\\u8BDD:\n\nWork_State\\: = \\u7701/\\u5E02/\\u533A:\n\nWork_Street_Address = \\u8857\\u9053\n\nWork_Zip_Code\\: = \\u90AE\\u653F\\u7F16\\u7801:\n\nYour_Email_Address = \\u7535\\u5B50\\u90AE\\u4EF6\\u5730\\u5740\n\n]_does_not_exist = ] \\u4E0D\\u5B58\\u5728\n\n]_is_not_a_directory = ] \\u4E0D\\u662F\\u4E00\\u4E2A\\u76EE\\u5F55\n\n__through__ = \\u81F3\n\naddcat = \\u6DFB\\u52A0\\u65B0\\u7C7B\\u522B\n\nappearance = \\u5916\\u89C2\\u663E\\u793A\n\napply = \\u5E94\\u7528\n\nappt_error = \\u7531\\u4E8E\\u51FA\\u73B0\\u4E00\\u4E2A\\u9519\\u8BEF\\uFF0C\\u7EA6\\u4F1A\\u7A97\\u53E3\\u5C06\\u5173\\u95ED\\u3002\\u8FD9\\u5C06\\u53EF\\u80FD\\u5BFC\\u81F4\\u4E00\\u4E2A\\u7EA6\\u4F1A\\u4FE1\\u606F\\u5728\\u4E0B\\u6B21\\u6253\\u5F00\\u65F6\\u4ECE\\u5F85\\u529E\\u4E8B\\u9879\\u7A97\\u53E3\\u4E2D\\u4E22\\u5931\\uFF0C\\u5426\\u5219\\uFF0C\\u5219\\u8BF7\\u91CD\\u65B0\\u542F\\u52A8Borg\n\napptlist = \\u7EA6\\u4F1A\\u5217\\u8868\n\nappttext = \\u7EA6\\u4F1A\\u5185\\u5BB9\n\nappttime = \\u7EA6\\u4F1A\\u65F6\\u95F4\n\nbad_db_2 = \\\\n\\u5982\\u679C\\u7531\\u4E8E\\u4E00\\u4E2A\\u6570\\u636E\\u5E93\\u76EE\\u5F55\\u5F15\\u53D1\\u4E86\\u9519\\u8BEF\\uFF0C\\u8BF7\\u5355\\u51FBYES\\u540E\\u4FEE\\u6539\\u5B83\\u3002\n\nbiweekly = \\u6BCF\\u4E24\\u5468\n\nblack = \\u9ED1\\u8272\n\nblue = \\u84DD\\u8272\n\ncatchooser = \\u7C7B\\u522B\\u9009\\u62E9\n\nchangedate = \\u66F4\\u6539\\u65E5\\u671F\n\nchoose_file = \\u9009\\u62E9\\u6587\\u4EF6\n\nchoosecat = \\u9009\\u62E9\\u5F53\\u524D\\u7C7B\\u522B\n\nclear_all = \\u5168\\u90E8\\u6E05\\u9664\n\ncontact = \\u8054\\u7CFB\\u4EBA\\u4FE1\\u606F\n\ndaily = \\u6BCF\\u65E5\n\ndb_set_to = \\\\n\\\\n\\u60A8\\u7684\\u6570\\u636E\\u5E93\\u76EE\\u5F55\\u5DF2\\u7ECF\\u88AB\\u8BBE\\u7F6E\\u4E3A: \n\ndel_tip = \\u5220\\u9664\\u91CD\\u590D\\u6027\\u7EA6\\u4F1A\\u4E2D\\u7684\\u6240\\u6709\\u5B9E\\u4F8B\n\ndelete_selected = \\u5220\\u9664\\u6240\\u9009\n\ndoo_tip = \\u5220\\u9664\\u91CD\\u590D\\u6027\\u7EA6\\u4F1A\\u4E2D\\u7684\\u5F53\\u524D\\u5B9E\\u4F8B\n\nedit_types = \\u7F16\\u8F91\\u4EFB\\u52A1\\u7684\\u7C7B\\u578B\\u4E0E\\u72B6\\u6001\n\nenable_popups = \\u5141\\u8BB8\\u4F7F\\u7528\\u6D6E\\u52A8\\u63D0\\u9192\\u7A97\\u53E3\\n\n\nenturl = \\u8F93\\u5165URL:\n\nep = \\u9009\\u9879\\u8BBE\\u7F6E\n\nexpXML = \\u5BFC\\u51FAXML...\n\nforever = \\u6CA1\\u6709\\u7ED3\\u675F\\u65E5\\u671F\n\ngreen = \\u7EFF\\u8272\n\nimpXML = \\u5BFC\\u5165XML...\n\nimpexpMenu = \\u5BFC\\u5165/\\u5BFC\\u51FA\n\nlocale = \\u672C\\u5730\\u8BED\\u8A00\\u652F\\u6301(\\u9700\\u8981\\u91CD\\u65B0\\u542F\\u52A8\\u7A0B\\u5E8F)\n\nmin_aft_app = \\u5728\\u7EA6\\u4F1A\\u5B8C\\u6210\\u4E4B\\u540E\n\nmin_bef_app = \\u5728\\u7EA6\\u4F1A\\u5230\\u6765\\u4E4B\\u524D\n\nmisc = \\u6742\\u9879\n\nmonthly = \\u6BCF\\u6708 (\\u56FA\\u5B9A\\u65E5)\n\nmonthly_day = \\u6BCF\\u6708 (\\u56FA\\u5B9A\\u661F\\u671F\\u51E0)\n\nmwf = \\u661F\\u671F\\u4E00\\u3001\\u4E09\\u3001\\u4E94\n\nnavy = navy\n\nndays = \\u6BCF\\u9694N\\u5929\n\nnewDate\\: = \\u65E5\\u671F:\n\nnummonths = \\u8981\\u6253\\u5370\\u7684\\u6708\\u6570\n\nonce = \\u4EC5\\u4E00\\u6B21\n\npopup_reminders = \\u63D0\\u9192\n\nred = \\u7EA2\\u8272\n\nremcat = \\u5220\\u9664\\u672A\\u4F7F\\u7528\\u7684\\u7C7B\\u522B\n\nreset_state_warning = \\u6B64\\u64CD\\u4F5C\\u91CD\\u7F6E\\u4EFB\\u52A1\\u72B6\\u6001\\u4FE1\\u606F\\u4E3A\\u7A0B\\u5E8F\\u539F\\u59CB\\u9ED8\\u8BA4\\u503C\\uFF0C\\\\n\\u8FD9\\u5C06\\u5BFC\\u81F4\\u6240\\u6709\\u81EA\\u5B9A\\u4E49\\u7684\\u4EFB\\u52A1\\u72B6\\u6001\\u4FE1\\u606F\\u5168\\u90E8\\u4E22\\u5931\\u3002\\\\n\\u786E\\u5B9A\\u8981\\u6267\\u884C\\u6B64\\u64CD\\u4F5C\\u5417?\\\\n\n\nsave_Def = \\u4FDD\\u5B58\\u4E3A\\u9ED8\\u8BA4\\u503C\n\nsd_tip = \\u5C06\\u5F53\\u524D\\u53D6\\u503C\\u4FDD\\u5B58\\u4E3A\\u65B0\\u7EA6\\u4F1A\\u7684\\u9ED8\\u8BA4\\u53D6\\u503C\n\nselect_all = \\u5168\\u90E8\\u9009\\u62E9\n\nselectdb = \\u5C1A\\u672A\\u914D\\u7F6E\\u6570\\u636E\\u5E93\\uFF0C\\u8BF7\\u8BBE\\u7F6E\\u6570\\u636E\\u5E93\\u73AF\\u5883\\u3002\n\nset_def_font = \\u8BBE\\u7F6E\\u9ED8\\u8BA4\\u5B57\\u4F53\n\nshowdoy = \\u663E\\u793A\\u6BCF\\u4E00\\u5929\\u5728\\u4E00\\u5E74\\u4E2D\\u7684\\u5E8F\\u53F7\n\nsplash = \\u663E\\u793A\\u542F\\u52A8\\u753B\\u9762\n\nsrch = \\u641C\\u7D22\n\nstackonerr = \\u5728\\u9519\\u8BEF\\u5BF9\\u8BDD\\u6846\\u4E2D\\u663E\\u793A\\u5806\\u6808\\u8DDF\\u8E2A\\u4FE1\\u606F\n\nstrike = \\u5220\\u9664\\u7EBF\n\ntodomissingdata = \\u8BF7\\u540C\\u65F6\\u8F93\\u5165\\u5F85\\u529E\\u4E8B\\u9879\\u7684\\u5185\\u5BB9\\u4E0E\\u65E5\\u671F.\n\ntodoquickentry = \\u5FEB\\u901F\\u6DFB\\u52A0\\u5F85\\u529E\\u4E8B\\u9879 *\n\ntth = \\u661F\\u671F\\u4E8C\\u3001\\u56DB\n\nuncategorized = <\\u672A\\u5B9A\\u4E49\\u7684\\u7C7B\\u522B>\n\nviewchglog = \\u67E5\\u770B\\u7248\\u672C\\u5386\\u53F2\n\nweekdays = \\u661F\\u671F\\u4E00\\u5230\\u661F\\u671F\\u4E94\n\nweekends = \\u661F\\u671F\\u516D\\u4E0E\\u661F\\u671F\\u65E5\n\nweekly = \\u6BCF\\u5468\n\nwhite = \\u767D\\u8272\n\nyearly = \\u6BCF\\u5E74\n"
  },
  {
    "path": "BORGCalendar/common/src/main/resources/properties",
    "content": "borg.version=2.0.0\nbuild.time=${timestamp}\nbuild.number=${buildNumber}"
  },
  {
    "path": "BORGCalendar/install/.gitignore",
    "content": "/.classpath\n"
  },
  {
    "path": "BORGCalendar/install/README.txt",
    "content": "Installers are now created using jpackage. IzPack is no longer used.\n\nInstallers need to be created manually by running either winpackage.bat on windows or linpackage.sh on linux.\n\nA tar or zip can be created from the contents of target/installer for other OSes, like MAC.\n\n** The installers bundle Java. A tar or ZIP does not.\n"
  },
  {
    "path": "BORGCalendar/install/build.xml",
    "content": "<?xml version=\"1.0\"?>\n<project name=\"borg-calendar-installer\" default=\"main\" basedir=\".\">\n\n\n\t<target name=\"init\">\n\t\t<tstamp />\n\t</target>\n\n\t<target name=\"copy\">\n\n\t\t<mkdir dir=\"target/installer\" />\n\t\t<mkdir dir=\"target/installer/lib\" />\n\t\t<mkdir dir=\"target/installer/licenses\" />\n\t\t<copy file=\"../swingui/src/main/resources/resource/RELEASE_NOTES.txt\" todir=\"target/installer\" />\n\t\t<copy file=\"../swingui/src/main/resources/resource/borglicense.txt\" todir=\"target/installer\" />\n\t\t<copy file=\"../swingui/src/main/resources/resource/COPYING\" todir=\"target/installer\" />\n\t\t<copy file=\"../swingui/src/main/resources/resource/borg.jpg\" todir=\"target/installer\" />\n\n\t\t<copy todir=\"target/installer/lib\">\n\t\t\t<fileset dir=\"../swingui/target/lib\" includes=\"*\" />\n\t\t</copy>\n\t\t<copy todir=\"target/installer/licenses\">\n\t\t\t<fileset dir=\"src/main/resources/licenses\" includes=\"*\" />\n\t\t</copy>\n\t\t\n\t\t<copy file=\"../swingui/target/swingui-${project.version}.jar\" tofile=\"target/installer/borg.jar\" />\n\n\t</target>\n\n\n\t<target name=\"main\" depends=\"init,copy\" />\n\n\n</project>\n"
  },
  {
    "path": "BORGCalendar/install/linpackage.sh",
    "content": "jpackage -t deb --app-version 2.0.0 -n BorgCalendar --vendor MBCSoft -i target/installer --main-class net.sf.borg.control.Borg --main-jar borg.jar --linux-menu-group Borg --linux-shortcut\n"
  },
  {
    "path": "BORGCalendar/install/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>BORGCalendar</groupId>\n\t\t<artifactId>BORGCalendar</artifactId>\n\t\t<version>2.0</version>\n\t</parent>\n\t<artifactId>install</artifactId>\n\t<packaging>jar</packaging>\n\n\t\n\t<build>\n\t\t<plugins>\n\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-resources-plugin</artifactId>\n\t\t\t\t<version>2.6</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>copy-resources</id>\n\t\t\t\t\t\t<phase>process-resources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>copy-resources</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<outputDirectory>${basedir}/target/installer</outputDirectory>\n\t\t\t\t\t\t\t<resources>\n\t\t\t\t\t\t\t\t<resource>\n\t\t\t\t\t\t\t\t\t<directory>src/main/resources</directory>\n\t\t\t\t\t\t\t\t\t<filtering>true</filtering>\n\t\t\t\t\t\t\t\t</resource>\n\t\t\t\t\t\t\t</resources>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t\n\t\t\t\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-antrun-plugin</artifactId>\n\t\t\t\t<version>1.7</version><!--$NO-MVN-MAN-VER$ -->\n\t\t\t\t<inherited>false</inherited>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>default-cli</id>\n\t\t\t\t\t\t<phase>process-resources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>run</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<target>\n\t\t\t\t\t\t\t\t<ant />\n\t\t\t\t\t\t\t</target>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t</plugins>\n\n\t</build>\n\n</project>"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/LICENSE.jnlf",
    "content": "\n\nJNLF Icons License:\n\nCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. \n\nSun grants you (\"Licensee\") a non-exclusive, royalty free, license to use, and redistribute this software graphics artwork, as individual graphics or as a collection, as part of software code or programs that you develop, provided that i) this copyright notice and license accompany the software graphics artwork; and ii) you do not utilize the software graphics artwork in a manner which is disparaging to Sun. Unless enforcement is prohibited by applicable law, you may not modify the graphics, and must use them true to color and unmodified in every way. \nThis software graphics artwork is provided \"AS IS,\" without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE GRAPHICS ARTWORK. \nIN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE GRAPHICS ARTWORK, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \nIf any of the above provisions are held to be in violation of applicable law, void, or unenforceable in any jurisdiction, then such provisions are waived to the extent necessary for this Disclaimer to be otherwise enforceable in such jurisdiction. "
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/THIRD-PARTY.txt",
    "content": "Lists of 56 third-party dependencies.\n     (GNU General Public License - Version 2) common (BORGCalendar:common:2.0 - http://mbcsoft.com/common)\n     (GNU General Public License - Version 2) model (BORGCalendar:model:2.0 - http://mbcsoft.com/model)\n     (The Apache License, Version 2.0) FlatLaf (com.formdev:flatlaf:3.7 - https://github.com/JFormDesigner/FlatLaf)\n     (The Apache License, Version 2.0) FlatLaf IntelliJ Themes Pack (com.formdev:flatlaf-intellij-themes:3.7 - https://github.com/JFormDesigner/FlatLaf)\n     (The Apache Software License, Version 2.0) Google APIs Client Library for Java (com.google.api-client:google-api-client:2.8.1 - https://github.com/googleapis/google-api-java-client/google-api-client)\n     (The Apache Software License, Version 2.0) Calendar API v3-rev20251028-2.0.0 (com.google.apis:google-api-services-calendar:v3-rev20251028-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-calendar)\n     (The Apache Software License, Version 2.0) Google Drive API v3-rev20251114-2.0.0 (com.google.apis:google-api-services-drive:v3-rev20251114-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-drive)\n     (The Apache Software License, Version 2.0) Google Tasks API v1-rev20251102-2.0.0 (com.google.apis:google-api-services-tasks:v1-rev20251102-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-tasks)\n     (BSD New license) Google Auth Library for Java - Credentials (com.google.auth:google-auth-library-credentials:1.30.0 - https://github.com/googleapis/google-auth-library-java/google-auth-library-credentials)\n     (BSD New license) Google Auth Library for Java - OAuth2 HTTP (com.google.auth:google-auth-library-oauth2-http:1.30.0 - https://github.com/googleapis/google-auth-library-java/google-auth-library-oauth2-http)\n     (Apache 2.0) AutoValue Annotations (com.google.auto.value:auto-value-annotations:1.11.0 - https://github.com/google/auto/tree/main/value)\n     (The Apache Software License, Version 2.0) FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)\n     (Apache-2.0) Gson (com.google.code.gson:gson:2.11.0 - https://github.com/google/gson)\n     (Apache 2.0) error-prone annotations (com.google.errorprone:error_prone_annotations:2.35.1 - https://errorprone.info/error_prone_annotations)\n     (The Apache Software License, Version 2.0) Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.2 - https://github.com/google/guava/failureaccess)\n     (Apache License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:33.1.0-jre - https://github.com/google/guava)\n     (The Apache Software License, Version 2.0) Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)\n     (The Apache Software License, Version 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client)\n     (The Apache Software License, Version 2.0) Apache HTTP transport v2 for the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-apache-v2:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client-apache-v2)\n     (The Apache Software License, Version 2.0) GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client-gson)\n     (Apache License, Version 2.0) J2ObjC Annotations (com.google.j2objc:j2objc-annotations:3.0.0 - https://github.com/google/j2objc/)\n     (The Apache Software License, Version 2.0) Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.36.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client)\n     (The Apache Software License, Version 2.0) Java 6 (and higher) extensions to the Google OAuth Client Library for Java. (com.google.oauth-client:google-oauth-client-java6:1.39.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client-java6)\n     (The Apache Software License, Version 2.0) OAuth 2.0 verification code receiver for Google OAuth Client Library for Java. (com.google.oauth-client:google-oauth-client-jetty:1.39.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client-jetty)\n     (EPL 1.0) (MPL 2.0) H2 Database Engine (com.h2database:h2:2.4.240 - https://h2database.com)\n     (Eclipse Distribution License - v 1.0) Old JAXB Core (com.sun.xml.bind:jaxb-core:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/)\n     (Eclipse Distribution License - v 1.0) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/)\n     (GNU LESSER GENERAL PUBLIC LICENSE) JCalendar (com.toedter:jcalendar:1.4 - http://www.toedter.com/en/jcalendar/)\n     (Apache License, Version 2.0) Apache Commons Codec (commons-codec:commons-codec:1.11 - http://commons.apache.org/proper/commons-codec/)\n     (The Apache Software License, Version 2.0) Commons IO (commons-io:commons-io:2.3 - http://commons.apache.org/io/)\n     (The Apache Software License, Version 2.0) Apache Commons Logging (commons-logging:commons-logging:1.2 - http://commons.apache.org/proper/commons-logging/)\n     (Apache 2.0) io.grpc:grpc-api (io.grpc:grpc-api:1.70.0 - https://github.com/grpc/grpc-java)\n     (Apache 2.0) io.grpc:grpc-context (io.grpc:grpc-context:1.70.0 - https://github.com/grpc/grpc-java)\n     (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-api:0.31.1 - https://github.com/census-instrumentation/opencensus-java)\n     (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-contrib-http-util:0.31.1 - https://github.com/census-instrumentation/opencensus-java)\n     (EDL 1.0) Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.4 - https://github.com/jakartaee/jaf-api)\n     (Eclipse Distribution License - v 1.0) Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.4 - https://github.com/jakartaee/jaxb-api/jakarta.xml.bind-api)\n     (Common Development and Distribution License (CDDL) v1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp)\n     (GNU General Public License - Version 2 with the class path exception) JavaHelp API (javax.help:javahelp:2.0.05 - https://javahelp.dev.java.net/)\n     (CDDL) (GPLv2+CE) JavaMail API (compat) (javax.mail:mail:1.5.0-b01 - http://kenai.com/projects/javamail/mail)\n     (Eclipse Public License 1.0) JUnit (junit:junit:4.13.2 - http://junit.org)\n     (Apache License, Version 2.0) Apache Commons Collections (org.apache.commons:commons-collections4:4.1 - http://commons.apache.org/proper/commons-collections/)\n     (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.8.1 - http://commons.apache.org/proper/commons-lang/)\n     (Apache License, Version 2.0) Apache HttpClient (org.apache.httpcomponents:httpclient:4.5.14 - http://hc.apache.org/httpcomponents-client-ga)\n     (Apache License, Version 2.0) Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga)\n     (The MIT License) Checker Qual (org.checkerframework:checker-qual:3.42.0 - https://checkerframework.org/)\n     (GNU Lesser General Public License Version 3) Font Chooser (org.drjekyll:fontchooser:3.1.0 - https://github.com/dheid/fontchooser)\n     (EDL 1.0) Angus Activation Registries (org.eclipse.angus:angus-activation:2.0.3 - https://github.com/eclipse-ee4j/angus-activation/angus-activation)\n     (New BSD License) Hamcrest Core (org.hamcrest:hamcrest-core:1.3 - https://github.com/hamcrest/JavaHamcrest/hamcrest-core)\n     (iCal4j - License) ical4j (org.mnode.ical4j:ical4j:2.2.5 - http://ical4j.github.io)\n     (iCal4j - License) ical4j-vcard (org.mnode.ical4j:ical4j-vcard:1.0.1 - http://ical4j.github.io)\n     (The MIT License) Project Lombok (org.projectlombok:lombok:1.18.38 - https://projectlombok.org)\n     (MIT License) JCL 1.1.1 implemented over SLF4J (org.slf4j:jcl-over-slf4j:1.7.14 - http://www.slf4j.org)\n     (MIT License) SLF4J API Module (org.slf4j:slf4j-api:1.7.25 - http://www.slf4j.org)\n     (BSD 3-clause) ThreeTen backport (org.threeten:threetenbp:1.3.6 - https://www.threeten.org/threetenbp)\n     (The Apache Software License, Version 2.0) SQLite JDBC (org.xerial:sqlite-jdbc:3.51.1.0 - https://github.com/xerial/sqlite-jdbc)\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/apache 2.0 - apache-2.0.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>Apache License, Version 2.0 &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fapache-2-0\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fapache-2-0&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-preformatted-inline-css' type='text/css'>\n.wp-block-preformatted{box-sizing:border-box;white-space:pre-wrap}:where(.wp-block-preformatted.has-background){padding:1.25em 2.375em}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/preformatted/style.css */\n</style>\n<style id='wp-block-quote-inline-css' type='text/css'>\n.wp-block-quote{box-sizing:border-box;overflow-wrap:break-word}.wp-block-quote.is-large:where(:not(.is-style-plain)),.wp-block-quote.is-style-large:where(:not(.is-style-plain)){margin-bottom:1em;padding:0 1em}.wp-block-quote.is-large:where(:not(.is-style-plain)) p,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) p{font-size:1.5em;font-style:italic;line-height:1.6}.wp-block-quote.is-large:where(:not(.is-style-plain)) cite,.wp-block-quote.is-large:where(:not(.is-style-plain)) footer,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) cite,.wp-block-quote.is-style-large:where(:not(.is-style-plain)) footer{font-size:1.125em;text-align:right}.wp-block-quote>cite{display:block}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/quote/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='wp-block-heading-inline-css' type='text/css'>\nh1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/heading/style.css */\n</style>\n<style id='wp-block-list-inline-css' type='text/css'>\nol,ul{box-sizing:border-box}:root :where(.wp-block-list.has-background){padding:1.25em 2.375em}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/list/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.2.0' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://c0.wp.com/p/jetpack/15.3.1/_inc/genericons/genericons/genericons.css' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"7d68a4059a\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1764960125\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/369\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/apache-2-0\" />\n<link rel='shortlink' href='https://opensource.org/?p=369' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"Apache License, Version 2.0\" />\n\t<meta property=\"og:description\" content=\"TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. &#8220;License&#8221; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/apache-2-0/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta property=\"og:image\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2006/07/OSI_Approved_License.png?fit=873%2C1200&amp;quality=80&amp;ssl=1\" />\n\t<meta property=\"og:image:width\" content=\"873\" />\n\t<meta property=\"og:image:height\" content=\"1200\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<meta name=\"twitter:image\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2006/07/OSI_Approved_License.png?fit=873%2C1200&amp;quality=80&amp;ssl=1\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-369 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-369\" class=\"post-369 license type-license status-publish has-post-thumbnail hentry taxonomy-steward-apache-software-foundation taxonomy-license-category-popular-strong-community\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Popular / Strong Community\" href=\"https://opensource.org/licenses/?categories=popular-strong-community\" data-id=\"19\" data-term=\"popular-strong-community\">Popular / Strong Community</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">Apache License, Version 2.0</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t<span class=\"license-version\">Version 2.0</span><span class=\"license-release\">Submitted: <a href=\"http://lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2004-February/007654.html\" target=\"_blank\">February 8, 2004</a></span><span class=\"license-submitter\">Submitter: Kevin Coar</span>\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tApache-2.0\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"license-steward-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-steward\">Steward:<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Apache Software Foundation\" href=\"https://opensource.org/license/steward/apache-software-foundation\" data-id=\"31\" data-term=\"apache-software-foundation\">Apache Software Foundation</a></li></ul></div>\n</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-steward-url\">\n\t\t\t\t\t\t\t\t\t<a href=\"https://www.apache.org/licenses/LICENSE-2.0\" target=\"_blank\">\n\t\t\t\t\t\t\t\t\t\tLink to license steward&#039;s version\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t\n<p>TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION</p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Definitions.</h3>\n\n\n\n<p>&#8220;License&#8221; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.</p>\n\n\n\n<p>&#8220;Licensor&#8221; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.</p>\n\n\n\n<p>&#8220;Legal Entity&#8221; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &#8220;control&#8221; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.</p>\n\n\n\n<p>&#8220;You&#8221; (or &#8220;Your&#8221;) shall mean an individual or Legal Entity exercising permissions granted by this License.</p>\n\n\n\n<p>&#8220;Source&#8221; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.</p>\n\n\n\n<p>&#8220;Object&#8221; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.</p>\n\n\n\n<p>&#8220;Work&#8221; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).</p>\n\n\n\n<p>&#8220;Derivative Works&#8221; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.</p>\n\n\n\n<p>&#8220;Contribution&#8221; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &#8220;submitted&#8221; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &#8220;Not a Contribution.&#8221;</p>\n\n\n\n<p>&#8220;Contributor&#8221; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.</p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Grant of Copyright License.</h3>\n\n\n\n<p>Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.</p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Grant of Patent License.</h3>\n\n\n\n<p>Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.</p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Redistribution.</h3>\n\n\n\n<p>You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:</p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>You must give any other recipients of the Work or Derivative Works a copy of this License; and</li>\n\n\n\n<li>You must cause any modified files to carry prominent notices stating that You changed the files; and</li>\n\n\n\n<li>You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and</li>\n\n\n\n<li>If the Work includes a &#8220;NOTICE&#8221; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.</li>\n</ol>\n\n\n\n<p>You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.</p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Submission of Contributions.</h3>\n\n\n\n<p>Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.</p>\n\n\n\n<h3 class=\"wp-block-heading\">6. Trademarks.</h3>\n\n\n\n<p>This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.</p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Disclaimer of Warranty.</h3>\n\n\n\n<p>Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &#8220;AS IS&#8221; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.</p>\n\n\n\n<h3 class=\"wp-block-heading\">8. Limitation of Liability.</h3>\n\n\n\n<p>In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.</p>\n\n\n\n<h3 class=\"wp-block-heading\">9. Accepting Warranty or Additional Liability.</h3>\n\n\n\n<p>While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.</p>\n\n\n\n<p>END OF TERMS AND CONDITIONS</p>\n\n\n\n<h2 class=\"wp-block-heading\" style=\"font-style:normal;font-weight:400;text-transform:none\">APPENDIX: How to apply the Apache License to your work</h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"is-style-default\">To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets &#8220;[]&#8221; replaced with your own identifying information. (Don&#8217;t include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same &#8220;printed page&#8221; as the copyright notice for easier identification within third-party archives.</p>\n\n\n\n<pre class=\"wp-block-preformatted\">   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</pre>\n</blockquote>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-369 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1764773745495\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1764773747362\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1764773745605\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%227d68a4059a%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"369\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3.1\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"369\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"124\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=124\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/blog/dpgas-annual-members-meeting-advancing-open-source-dpgs-for-the-public-good\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e1683fa42da97',t:'MTc2NTA0ODI3NA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated 239 seconds ago\n\tgenerated in 0.679 seconds\n\tserved from batcache in 0.004 seconds\n\texpires in 61 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/bsd 3-clause - license.txt",
    "content": "Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of JSR-310 nor the names of its contributors\n  may be used to endorse or promote products derived from this software\n  without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/bsd new license - bsd-3-clause.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>The 3-Clause BSD License &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fbsd-3-clause\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fbsd-3-clause&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-separator-inline-css' type='text/css'>\n@charset \"UTF-8\";.wp-block-separator{border:none;border-top:2px solid}:root :where(.wp-block-separator.is-style-dots){height:auto;line-height:1;text-align:center}:root :where(.wp-block-separator.is-style-dots):before{color:currentColor;content:\"···\";font-family:serif;font-size:1.5em;letter-spacing:2em;padding-left:2em}.wp-block-separator.is-style-dots{background:none!important;border:none!important}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/separator/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.2.0' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://c0.wp.com/p/jetpack/15.3.1/_inc/genericons/genericons/genericons.css' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"7d68a4059a\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1764960125\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/657\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/bsd-3-clause\" />\n<link rel='shortlink' href='https://opensource.org/?p=657' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"The 3-Clause BSD License\" />\n\t<meta property=\"og:description\" content=\"Note: This license has also been called the &#8220;New BSD License&#8221; or &#8220;Modified BSD License&#8221;. See also the 2-clause BSD License. Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt; Redistribution and use in source…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/BSD-3-Clause/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-657 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-657\" class=\"post-657 license type-license status-publish hentry taxonomy-license-category-popular-strong-community\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Popular / Strong Community\" href=\"https://opensource.org/licenses/?categories=popular-strong-community\" data-id=\"19\" data-term=\"popular-strong-community\">Popular / Strong Community</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">The 3-Clause BSD License</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tBSD-3-Clause\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"/>\n\n\n\n<p><em>Note: This license has also been called the &#8220;New BSD License&#8221; or &#8220;Modified BSD License&#8221;. See also the <a href=\"https://opensource.org/license/bsd-2-clause/\" data-type=\"license\" data-id=\"373\">2-clause BSD License</a>.</em></p>\n\n\n\n<p>Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt;</p>\n\n\n\n<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>\n\n\n\n<p>1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</p>\n\n\n\n<p>2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.</p>\n\n\n\n<p>3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.</p>\n\n\n\n<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &#8220;AS IS&#8221; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-657 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1764773745495\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1764773747362\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1764773745605\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%227d68a4059a%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"657\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3.1\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"657\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"124\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=124\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/license/bsd-3-clause\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e167889c8da97',t:'MTc2NTA0ODI3Mg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated 224 seconds ago\n\tgenerated in 0.837 seconds\n\tserved from batcache in 0.003 seconds\n\texpires in 76 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/cddl - cddl.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\" class=\"no-js\"><!-- start : Framework/Redwood2 -->\n<!-- start : ocom/common/global/components/framework/head -->\n<head>\n<!-- start : ocom/common/global/components/head -->\n<title>Hardware | Oracle</title>\n<meta name=\"Title\" content=\"Hardware | Oracle\">\n<meta name=\"Description\" content=\"Lower total cost of ownership with powerful on-premises Oracle hardware solutions that include unique Oracle Database optimizations and Oracle Cloud Infrastructure integrations.\">\n<meta name=\"Keywords\" content=\"hardware, IT Infrastructure, infrastructure cloud, on-premises hardware, on-premises infrastructure, servers\">\n<meta name=\"resgen\" content=\"No\">\n<meta name=\"siteid\" content=\"us\">\n<meta name=\"countryid\" content=\"US\">\n<meta name=\"robots\" content=\"index, follow\">\n<meta name=\"country\" content=\"United States\">\n<meta name=\"region\" content=\"americas\">\n<meta name=\"Language\" content=\"en\">\n<meta name=\"Updated Date\" content=\"2025-11-05T20:17:14Z\">\n<meta name=\"page_type\" content=\"Product-Pillar-Overview\">\n<script type=\"text/javascript\">\nvar pageData = pageData || {};\n//page info\npageData.pageInfo = pageData.pageInfo || {};\npageData.pageInfo.language = \"en\";\npageData.pageInfo.country = \"United States\";\npageData.pageInfo.region = \"americas\";\npageData.pageInfo.pageTitle = \"Hardware | Oracle\";\npageData.pageInfo.description = \"Lower total cost of ownership with powerful on-premises Oracle hardware solutions that include unique Oracle Database optimizations and Oracle Cloud Infrastructure integrations.\";\npageData.pageInfo.keywords = \"hardware, IT Infrastructure, infrastructure cloud, on-premises hardware, on-premises infrastructure, servers\";\npageData.pageInfo.siteId = \"us\";\npageData.pageInfo.countryId = \"US\";\npageData.pageInfo.updatedDate = \"2025-11-05T20:17:14Z\";\n</script>\n<!-- start : ocom/common/global/components/seo-taxonomy-meta-data-->\n<script type=\"text/javascript\">\nvar pageData = pageData || {};\n//taxonmy\npageData.taxonomy = pageData.taxonomy || {};\npageData.taxonomy.productServices = [];\npageData.taxonomy.industry = [];\npageData.taxonomy.region = [];\npageData.taxonomy.contentType = [\"WM147085\"];\npageData.taxonomy.buyersJourney = [];\npageData.taxonomy.revenue = [];\npageData.taxonomy.title = [];\npageData.taxonomy.audience = [];\npageData.taxonomy.persona = [];\n</script>\n<!-- end : ocom/common/global/components/seo-taxonomy-meta-data-->\n<!-- start : ocom/common/global/components/social-media-meta-tags --><!-- F13v0 -->\n<meta property=\"og:title\" content=\"Explore Oracle Hardware\"/>\n<meta property=\"og:description\" content=\"Lower TCO with powerful, on-premise  Oracle hardware solutions that include unique Oracle Database optimizations and Oracle Cloud integrations.\"/>\n<meta property=\"og:url\" content=\"https://www.oracle.com/it-infrastructure/\"/>\n<meta property=\"og:image\" content=\"https://www.oracle.com/a/ocom/img/social-og-oracle-badge.jpg\"/>\n<!-- /F13v0 -->\n<!-- F13v1 -->\n<meta name=\"twitter:card\" content=\"summary\">\n<meta name=\"twitter:title\" content=\"Explore Oracle Hardware\">\n<meta name=\"twitter:description\" content=\"Lower TCO with powerful, on-premise  Oracle hardware solutions that include unique Oracle Database optimizations and Oracle Cloud integrations.\">\n<meta name=\"twitter:url\" content=\"https://www.oracle.com/it-infrastructure/\">\n<meta name=\"twitter:image\" content=\"https://www.oracle.com/a/ocom/img/social-og-oracle-badge.jpg\">\n<!-- /F13v1 -->\n<!-- end : ocom/common/global/components/social-media-meta-tags -->\n<!-- start : ocom/common/global/components/seo-hreflang-meta-tags --><link rel=\"canonical\" href=\"https://www.oracle.com/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-US\" href=\"https://www.oracle.com/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"pt-BR\" href=\"https://www.oracle.com/br/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"zh-CN\" href=\"https://www.oracle.com/cn/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"de-DE\" href=\"https://www.oracle.com/de/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"fr-FR\" href=\"https://www.oracle.com/fr/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-IN\" href=\"https://www.oracle.com/in/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"ja-JP\" href=\"https://www.oracle.com/jp/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-GB\" href=\"https://www.oracle.com/uk/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-AE\" href=\"https://www.oracle.com/ae/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"ar-AE\" href=\"https://www.oracle.com/ae-ar/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"es-AR\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-AU\" href=\"https://www.oracle.com/au/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-CA\" href=\"https://www.oracle.com/ca-en/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"fr-CA\" href=\"https://www.oracle.com/ca-fr/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"de-CH\" href=\"https://www.oracle.com/de/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"fr-CH\" href=\"https://www.oracle.com/fr/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"es-CL\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"es-CO\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"es-ES\" href=\"https://www.oracle.com/es/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-HK\" href=\"https://www.oracle.com/apac/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"it-IT\" href=\"https://www.oracle.com/it/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"ko-KR\" href=\"https://www.oracle.com/kr/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"es-MX\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"nl-NL\" href=\"https://www.oracle.com/nl/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-SA\" href=\"https://www.oracle.com/sa/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"ar-SA\" href=\"https://www.oracle.com/sa-ar/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-SG\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"zh-TW\" href=\"https://www.oracle.com/tw/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"cs-CZ\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-ID\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-IE\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"he-IL\" href=\"https://www.oracle.com/il/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-IL\" href=\"https://www.oracle.com/il-en/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-MY\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"es-PE\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-PH\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"pl-PL\" href=\"https://www.oracle.com/pl/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"pt-PT\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ro-RO\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"sv-SE\" href=\"https://www.oracle.com/se/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"tr-TR\" href=\"https://www.oracle.com/tr/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-ZA\" href=\"https://www.oracle.com/africa/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"de-AT\" href=\"https://www.oracle.com/de/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-BA\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-BD\" href=\"https://www.oracle.com/apac/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-BE\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"bg-BG\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-BH\" href=\"https://www.oracle.com/middleeast/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ar-BH\" href=\"https://www.oracle.com/middleeast-ar/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-BZ\" href=\"https://www.oracle.com/bz/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"es-CR\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-CY\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"da-DK\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"fr-DZ\" href=\"https://www.oracle.com/africa-fr/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-EE\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-EG\" href=\"https://www.oracle.com/middleeast/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ar-EG\" href=\"https://www.oracle.com/middleeast-ar/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"fi-FI\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-GH\" href=\"https://www.oracle.com/africa/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"el-GR\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"hr-HR\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"hu-HU\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-JO\" href=\"https://www.oracle.com/middleeast/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ar-JO\" href=\"https://www.oracle.com/middleeast-ar/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-KE\" href=\"https://www.oracle.com/africa/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-KW\" href=\"https://www.oracle.com/middleeast/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ar-KW\" href=\"https://www.oracle.com/middleeast-ar/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-LK\" href=\"https://www.oracle.com/apac/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-LU\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"lv-LV\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"fr-MA\" href=\"https://www.oracle.com/africa-fr/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-NG\" href=\"https://www.oracle.com/africa/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"no-NO\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-NZ\" href=\"https://www.oracle.com/nz/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"en-QA\" href=\"https://www.oracle.com/middleeast/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"ar-QA\" href=\"https://www.oracle.com/middleeast-ar/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"sr-RS\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"sl-SI\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"sk-SK\" href=\"https://www.oracle.com/europe/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"fr-SN\" href=\"https://www.oracle.com/africa-fr/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-TH\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"uk-UA\" href=\"https://www.oracle.com/ua/it-infrastructure/\" />\n<link rel=\"alternate\" hreflang=\"es-UY\" href=\"https://www.oracle.com/latam/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"en-VN\" href=\"https://www.oracle.com/asean/it-infrastructure/\"/>\n<link rel=\"alternate\" hreflang=\"lt-LT\" href=\"https://www.oracle.com/lt/it-infrastructure/\" />\n<meta name=\"altpages\" content=\"br,cn,de,fr,in,jp,uk,ae,ae-ar,ar,au,ca-en,ca-fr,ch-de,ch-fr,cl,co,es,hk,it,kr,mx,nl,sa,sa-ar,sg,tw,cz,id,ie,il,il-en,my,pe,ph,pl,pt,ro,se,tr,za,at,ba,bd,be,bg,bh,bh-ar,bz,cr,cy,dk,dz,ee,eg,eg-ar,fi,gh,gr,hr,hu,jo,jo-ar,ke,kw,kw-ar,lk,lu,lv,ma,ng,no,nz,qa,qa-ar,rs,si,sk,sn,th,ua,uy,vn,lt\">\n<script type=\"text/javascript\">\nvar pageData = pageData || {};\n//page info\npageData.pageInfo = pageData.pageInfo || {};\npageData.pageInfo.altPages = \"br,cn,de,fr,in,jp,uk,ae,ae-ar,ar,au,ca-en,ca-fr,ch-de,ch-fr,cl,co,es,hk,it,kr,mx,nl,sa,sa-ar,sg,tw,cz,id,ie,il,il-en,my,pe,ph,pl,pt,ro,se,tr,za,at,ba,bd,be,bg,bh,bh-ar,bz,cr,cy,dk,dz,ee,eg,eg-ar,fi,gh,gr,hr,hu,jo,jo-ar,ke,kw,kw-ar,lk,lu,lv,ma,ng,no,nz,qa,qa-ar,rs,si,sk,sn,th,ua,uy,vn,lt\";\n</script>\n<!-- end : ocom/common/global/components/seo-hreflang-meta-tags -->\n<!-- end : ocom/common/global/components/head -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<!-- start : ocom/common/global/components/framework/layoutAssetHeadInfo -->\n<!-- Container/Display Start -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<link rel=\"dns-prefetch\" href=\"https://dc.oracleinfinity.io\" crossOrigin=\"anonymous\" />\n<link rel=\"dns-prefetch\" href=\"https://oracle.112.2o7.net\" crossOrigin=\"anonymous\" />\n<link rel=\"dns-prefetch\" href=\"https://s.go-mpulse.net\" crossOrigin=\"anonymous\" />\n<link rel=\"dns-prefetch\" href=\"https://c.go-mpulse.net\" crossOrigin=\"anonymous\" />\n<link rel=\"preconnect\" href=\"https://tms.oracle.com/\" crossOrigin=\"anonymous\" />\n<link rel=\"preconnect\" href=\"https://tags.tiqcdn.com/\" crossOrigin=\"anonymous\" />\n<link rel=\"preconnect\" href=\"https://consent.trustarc.com\" crossOrigin=\"anonymous\" />\n<link rel=\"preconnect\" href=\"https://d.oracleinfinity.io\" crossOrigin=\"anonymous\" />\n<link rel=\"preload\" href=\"https://www.oracle.com/asset/web/fonts/oraclesansvf.woff2\" as=\"font\" crossOrigin=\"anonymous\" type=\"font/woff2\" />\n<link rel=\"preload\" href=\"https://www.oracle.com/asset/web/fonts/redwoodicons.woff2\" as=\"font\" crossorigin=\"anonymous\" type=\"font/woff2\">\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Container/Display end -->\n<!-- Container/Display Start -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- BEGIN CSS/JS -->\n<style>\nbody{margin:0}\n.f20w1 > section{opacity:0;filter:opacity(0%)}\n.f20w1 > div{opacity:0;filter:opacity(0%)}\n.f20w1 > nav{opacity:0;filter:opacity(0%)}\n</style>\n<script>\n!function(){\nvar d=document.documentElement;d.className=d.className.replace(/no-js/,'js');\nif(document.location.href.indexOf('betamode=') > -1) document.write('<script src=\"/asset/web/js/ocom-betamode.js\"><\\/script>');\n}();\n</script>\n<link data-wscss href=\"/asset/web/css/redwood-base.css\" rel=\"preload\" as=\"style\" onload=\"this.rel='stylesheet';\" onerror=\"this.rel='stylesheet'\">\n<link data-wscss href=\"/asset/web/css/redwood-styles.css\" rel=\"preload\" as=\"style\" onload=\"this.rel='stylesheet';\" onerror=\"this.rel='stylesheet'\">\n<noscript>\n<link href=\"/asset/web/css/redwood-base.css\" rel=\"stylesheet\">\n<link href=\"/asset/web/css/redwood-styles.css\" rel=\"stylesheet\">\n</noscript>\n<link data-wsjs data-reqjq href=\"/asset/web/js/redwood-base.js\" rel=\"preload\" as=\"script\">\n<link data-wsjs data-reqjq href=\"/asset/web/js/redwood-lib.js\" rel=\"preload\" as=\"script\">\n<script data-wsjs src=\"/asset/web/js/jquery-min.js\" async onload=\"$('head link[data-reqjq][rel=preload]').each(function(){var a = document.createElement('script');a.async=false;a.src=$(this).attr('href');this.parentNode.insertBefore(a, this);});$(function(){$('script[data-reqjq][data-src]').each(function(){this.async=true;this.src=$(this).data('src');});});\"></script>\n<!-- END CSS/JS -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Container/Display end -->\n<!-- Container/Display Start -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- www-us HeadAdminContainer -->\n<link rel=\"icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-32.png\" sizes=\"32x32\">\n<link rel=\"icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-128.png\" sizes=\"128x128\">\n<link rel=\"icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-192.png\" sizes=\"192x192\">\n<link rel=\"apple-touch-icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-120.png\" sizes=\"120x120\">\n<link rel=\"apple-touch-icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-152.png\" sizes=\"152x152\">\n<link rel=\"apple-touch-icon\" href=\"https://www.oracle.com/asset/web/favicons/favicon-180.png\" sizes=\"180x180\">\n<meta name=\"msapplication-TileColor\" content=\"#fcfbfa\"/>\n<meta name=\"msapplication-square70x70logo\" content=\"favicon-128.png\"/>\n<meta name=\"msapplication-square150x150logo\" content=\"favicon-270.png\"/>\n<meta name=\"msapplication-TileImage\" content=\"favicon-270.png\"/>\n<meta name=\"msapplication-config\" content=\"none\"/>\n<meta name=\"referrer\" content=\"no-referrer-when-downgrade\"/>\n<style>\n#oReactiveChatContainer1 { display:none;}\n</style>\n<!--Tealium embed code Start -->\n<script src=\"https://tms.oracle.com/main/prod/utag.sync.js\"></script>\n<!-- Loading script asynchronously -->\n<script type=\"text/javascript\">\n(function(a,b,c,d){\nif(location.href.indexOf(\"tealium=dev\") == -1) {\na='https://tms.oracle.com/main/prod/utag.js';\n} else {\na='https://tms.oracle.com/main/dev/utag.js';\n}\nb=document;c='script';d=b.createElement(c);d.src=a;d.type='text/java'+c;d.async=true;\na=b.getElementsByTagName(c)[0];a.parentNode.insertBefore(d,a);\n})();\n</script>\n<!--Tealium embed code End-->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Container/Display end -->\n<!-- end : ocom/common/global/components/framework/layoutAssetHeadInfo -->\n\n<script>(window.BOOMR_mq=window.BOOMR_mq||[]).push([\"addVar\",{\"rua.upush\":\"false\",\"rua.cpush\":\"false\",\"rua.upre\":\"false\",\"rua.cpre\":\"false\",\"rua.uprl\":\"false\",\"rua.cprl\":\"false\",\"rua.cprf\":\"false\",\"rua.trans\":\"\",\"rua.cook\":\"false\",\"rua.ims\":\"false\",\"rua.ufprl\":\"false\",\"rua.cfprl\":\"false\",\"rua.isuxp\":\"false\",\"rua.texp\":\"norulematch\",\"rua.ceh\":\"false\",\"rua.ueh\":\"false\",\"rua.ieh.st\":\"0\"}]);</script>\n                              <script>!function(e){var n=\"https://s.go-mpulse.net/boomerang/\";if(\"False\"==\"True\")e.BOOMR_config=e.BOOMR_config||{},e.BOOMR_config.PageParams=e.BOOMR_config.PageParams||{},e.BOOMR_config.PageParams.pci=!0,n=\"https://s2.go-mpulse.net/boomerang/\";if(window.BOOMR_API_key=\"DXNLE-YBWWY-AR74T-WMD99-77VRA\",function(){function e(){if(!o){var e=document.createElement(\"script\");e.id=\"boomr-scr-as\",e.src=window.BOOMR.url,e.async=!0,i.parentNode.appendChild(e),o=!0}}function t(e){o=!0;var n,t,a,r,d=document,O=window;if(window.BOOMR.snippetMethod=e?\"if\":\"i\",t=function(e,n){var t=d.createElement(\"script\");t.id=n||\"boomr-if-as\",t.src=window.BOOMR.url,BOOMR_lstart=(new Date).getTime(),e=e||d.body,e.appendChild(t)},!window.addEventListener&&window.attachEvent&&navigator.userAgent.match(/MSIE [67]\\./))return window.BOOMR.snippetMethod=\"s\",void t(i.parentNode,\"boomr-async\");a=document.createElement(\"IFRAME\"),a.src=\"about:blank\",a.title=\"\",a.role=\"presentation\",a.loading=\"eager\",r=(a.frameElement||a).style,r.width=0,r.height=0,r.border=0,r.display=\"none\",i.parentNode.appendChild(a);try{O=a.contentWindow,d=O.document.open()}catch(_){n=document.domain,a.src=\"javascript:var d=document.open();d.domain='\"+n+\"';void(0);\",O=a.contentWindow,d=O.document.open()}if(n)d._boomrl=function(){this.domain=n,t()},d.write(\"<bo\"+\"dy onload='document._boomrl();'>\");else if(O._boomrl=function(){t()},O.addEventListener)O.addEventListener(\"load\",O._boomrl,!1);else if(O.attachEvent)O.attachEvent(\"onload\",O._boomrl);d.close()}function a(e){window.BOOMR_onload=e&&e.timeStamp||(new Date).getTime()}if(!window.BOOMR||!window.BOOMR.version&&!window.BOOMR.snippetExecuted){window.BOOMR=window.BOOMR||{},window.BOOMR.snippetStart=(new Date).getTime(),window.BOOMR.snippetExecuted=!0,window.BOOMR.snippetVersion=12,window.BOOMR.url=n+\"DXNLE-YBWWY-AR74T-WMD99-77VRA\";var i=document.currentScript||document.getElementsByTagName(\"script\")[0],o=!1,r=document.createElement(\"link\");if(r.relList&&\"function\"==typeof r.relList.supports&&r.relList.supports(\"preload\")&&\"as\"in r)window.BOOMR.snippetMethod=\"p\",r.href=window.BOOMR.url,r.rel=\"preload\",r.as=\"script\",r.addEventListener(\"load\",e),r.addEventListener(\"error\",function(){t(!0)}),setTimeout(function(){if(!o)t(!0)},3e3),BOOMR_lstart=(new Date).getTime(),i.parentNode.appendChild(r);else t(!1);if(window.addEventListener)window.addEventListener(\"load\",a,!1);else if(window.attachEvent)window.attachEvent(\"onload\",a)}}(),\"\".length>0)if(e&&\"performance\"in e&&e.performance&&\"function\"==typeof e.performance.setResourceTimingBufferSize)e.performance.setResourceTimingBufferSize();!function(){if(BOOMR=e.BOOMR||{},BOOMR.plugins=BOOMR.plugins||{},!BOOMR.plugins.AK){var n=\"\"==\"true\"?1:0,t=\"\",a=\"ils7paixzwofm2jup7oq-f-a22e78a22-clientnsv4-s.akamaihd.net\",i=\"false\"==\"true\"?2:1,o={\"ak.v\":\"39\",\"ak.cp\":\"82485\",\"ak.ai\":parseInt(\"604074\",10),\"ak.ol\":\"0\",\"ak.cr\":18,\"ak.ipv\":4,\"ak.proto\":\"http/1.1\",\"ak.rid\":\"a04bc41f\",\"ak.r\":46503,\"ak.a2\":n,\"ak.m\":\"dscx\",\"ak.n\":\"essl\",\"ak.bpcip\":\"66.229.247.0\",\"ak.cport\":52258,\"ak.gh\":\"23.223.30.179\",\"ak.quicv\":\"\",\"ak.tlsv\":\"tls1.3\",\"ak.0rtt\":\"\",\"ak.0rtt.ed\":\"\",\"ak.csrc\":\"-\",\"ak.acc\":\"bbr\",\"ak.t\":\"1765048285\",\"ak.ak\":\"hOBiQwZUYzCg5VSAfCLimQ==4UX1bXxSFBKiy83vwwxZ5afktDUj5cknnorXOS0aw76wINoTVbGlkGQ7SMYUQDA/M3jV5L4006A7Q/hQ0zLCYIez90opwiAtFkcr1Q625FwvFkZpXH7Ivj1kc07i6lv9Z54UJ1Tv5AByvFR+/ohWwbWo5d8oc3wEjBsvEknKvTrX9LvCBZYNxT/J6tfSdHDA6unxWoavMFp3/cxN9aOA77IK8MzrpqR5KgILGjXLLVOh1fu9rrV92+nJZDX68WWb7qwawdcWgnceMIeVwN70eQkJiVbRU0q7jvYgDeqC5MWi05/fBf8cqwObArc75DsSMFURD/WOZEh8WDA7j3J8OF/7A3imp3ajQFp36c707U+TiYnCpA+THw323GUI1Y0e+82cBJo5jGHoKXAGiCUly1d4ofthwih8h9Hn/1W4sS4=\",\"ak.pv\":\"513\",\"ak.dpoabenc\":\"\",\"ak.tf\":i};if(\"\"!==t)o[\"ak.ruds\"]=t;var r={i:!1,av:function(n){var t=\"http.initiator\";if(n&&(!n[t]||\"spa_hard\"===n[t]))o[\"ak.feo\"]=void 0!==e.aFeoApplied?1:0,BOOMR.addVar(o)},rv:function(){var e=[\"ak.bpcip\",\"ak.cport\",\"ak.cr\",\"ak.csrc\",\"ak.gh\",\"ak.ipv\",\"ak.m\",\"ak.n\",\"ak.ol\",\"ak.proto\",\"ak.quicv\",\"ak.tlsv\",\"ak.0rtt\",\"ak.0rtt.ed\",\"ak.r\",\"ak.acc\",\"ak.t\",\"ak.tf\"];BOOMR.removeVar(e)}};BOOMR.plugins.AK={akVars:o,akDNSPreFetchDomain:a,init:function(){if(!r.i){var e=BOOMR.subscribe;e(\"before_beacon\",r.av,null,null),e(\"onbeacon\",r.rv,null,null),r.i=!0}return this},is_complete:function(){return!0}}}}()}(window);</script></head><!-- end : ocom/common/global/components/framework/head -->\n<!-- start : ocom/common/global/components/redwood/layoutAssetStartBodyInfo -->\n<body class=\"f20 f20v0 rwtheme-neutral\">\n<!-- Container/Display Start -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- www-us StartBodyAdminContainer -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Container/Display end -->\n<div class=\"f20w1\">\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- U30v3 -->\n<style>\n#u30{opacity:1 !important;filter:opacity(100%) !important;position:sticky;top:0}\n.u30v3{background:#3a3632;height:50px;overflow:hidden;border-top:5px solid #3a3632;border-bottom:5px solid #3a3632}\n#u30nav,#u30tools{visibility:hidden}\n.u30v3 #u30logo {width:121px;height: 44px;display: inline-flex;justify-content: flex-start;}\n#u30:not(.u30mobile) .u30-oicn-mobile,#u30.u30mobile .u30-oicn{display:none}\n#u30logo svg{height:auto;align-self:center}\n.u30brand{height:50px;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;max-width:1344px;padding:0 48px;margin:0 auto}\n.u30brandw1{display:flex;flex-direction:row;color:#fff;text-decoration:none;align-items:center}\n@media (max-width:1024px){.u30brand{padding:0 24px}}\n#u30skip2,#u30skip2content{transform:translateY(-100%);position:fixed}\n.rtl #u30{direction:rtl}\n</style>\n<section id=\"u30\" class=\"u30 u30v3 pause searchv2\" data-trackas=\"header\">\n<div id=\"u30skip2\">\n<ul>\n<li><a id=\"u30skip2c\" href=\"#maincontent\">Skip to content</a></li>\n<li><a id=\"u30acc\" href=\"https://www.oracle.com/corporate/accessibility/\">Accessibility Policy</a></li>\n</ul>\n</div>\n<div class=\"u30w1 cwidth\" id=\"u30w1\">\n<div id=\"u30brand\" class=\"u30brand\">\n<div class=\"u30brandw1\">\n<a id=\"u30btitle\" href=\"https://www.oracle.com/\" data-lbl=\"logo\" aria-label=\"Home\">\n<div id=\"u30logo\">\n<svg class=\"u30-oicn-mobile\" xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"21\" viewBox=\"0 0 32 21\"><path fill=\"#C74634\" d=\"M9.9,20.1c-5.5,0-9.9-4.4-9.9-9.9c0-5.5,4.4-9.9,9.9-9.9h11.6c5.5,0,9.9,4.4,9.9,9.9c0,5.5-4.4,9.9-9.9,9.9H9.9 M21.2,16.6c3.6,0,6.4-2.9,6.4-6.4c0-3.6-2.9-6.4-6.4-6.4h-11c-3.6,0-6.4,2.9-6.4,6.4s2.9,6.4,6.4,6.4H21.2\"/></svg>\n<svg class=\"u30-oicn\" xmlns=\"http://www.w3.org/2000/svg\"  width=\"231\" height=\"30\" viewBox=\"0 0 231 30\" preserveAspectRatio=\"xMinYMid\"><path fill=\"#C74634\" d=\"M99.61,19.52h15.24l-8.05-13L92,30H85.27l18-28.17a4.29,4.29,0,0,1,7-.05L128.32,30h-6.73l-3.17-5.25H103l-3.36-5.23m69.93,5.23V0.28h-5.72V27.16a2.76,2.76,0,0,0,.85,2,2.89,2.89,0,0,0,2.08.87h26l3.39-5.25H169.54M75,20.38A10,10,0,0,0,75,.28H50V30h5.71V5.54H74.65a4.81,4.81,0,0,1,0,9.62H58.54L75.6,30h8.29L72.43,20.38H75M14.88,30H32.15a14.86,14.86,0,0,0,0-29.71H14.88a14.86,14.86,0,1,0,0,29.71m16.88-5.23H15.26a9.62,9.62,0,0,1,0-19.23h16.5a9.62,9.62,0,1,1,0,19.23M140.25,30h17.63l3.34-5.23H140.64a9.62,9.62,0,1,1,0-19.23h16.75l3.38-5.25H140.25a14.86,14.86,0,1,0,0,29.71m69.87-5.23a9.62,9.62,0,0,1-9.26-7h24.42l3.36-5.24H200.86a9.61,9.61,0,0,1,9.26-7h16.76l3.35-5.25h-20.5a14.86,14.86,0,0,0,0,29.71h17.63l3.35-5.23h-20.6\" transform=\"translate(-0.02 0)\" /></svg>\n</div>\n</a>\n</div>\n</div>\n<div id=\"u30nav\" class=\"u30nav\" data-closetxt=\"Close Menu\" data-trackas=\"menu\">\n<nav id=\"u30navw1\" aria-label=\"Oracle website\">\n<button data-navtarget=\"products\" aria-controls=\"products\" aria-expanded=\"false\" class=\"u30navitem\">Products</button>\n<button data-navtarget=\"industries\" aria-controls=\"industries\" aria-expanded=\"false\" class=\"u30navitem\">Industries</button>\n<button data-navtarget=\"resources\" aria-controls=\"resources\" aria-expanded=\"false\" class=\"u30navitem\">Resources</button>\n<button data-navtarget=\"customers\" aria-controls=\"customers\" aria-expanded=\"false\" class=\"u30navitem\">Customers</button>\n<button data-navtarget=\"partners\" aria-controls=\"partners\" aria-expanded=\"false\" class=\"u30navitem\">Partners</button>\n<button data-navtarget=\"developers\" aria-controls=\"developers\" aria-expanded=\"false\" class=\"u30navitem\">Developers</button>\n<button data-navtarget=\"company\" aria-controls=\"company\" aria-expanded=\"false\" class=\"u30navitem\">Company</button>\n</nav>\n</div>\n<div id=\"u30tools\" class=\"u30tools\">\n<div id=\"u30search\">\n<div id=\"u30searchw1\">\n<div id=\"u30searchw2\">\n<form name=\"u30searchForm\" id=\"u30searchForm\"\ndata-contentpaths=\"/content/Web/Shared/Auto-Suggest Panel Event\" method=\"get\"\naction=\"https://search.oracle.com/results\">\n<div class=\"u30s1\">\n<button id=\"u30closesearch\" aria-label=\"Close Search\" type=\"button\">\n<span>Close Search</span>\n<svg width=\"9\" height=\"14\" viewBox=\"0 0 9 14\" fill=\"none\"\nxmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M8 13L2 7L8 1\" stroke=\"#161513\" stroke-width=\"2\" />\n</svg>\n</button>\n<span class=\"u30input\">\n<div class=\"u30inputw1\">\n<input id=\"u30input\" name=\"q\" value=\"\" type=\"text\" placeholder=\"Search\"\nautocomplete=\"off\" aria-autocomplete=\"both\" aria-label=\"Search Oracle.com\"\nrole=\"combobox\" aria-expanded=\"false\" aria-haspopup=\"listbox\"\naria-controls=\"u30searchw3\">\n</div>\n<input type=\"hidden\" name=\"size\" value=\"10\">\n<input type=\"hidden\" name=\"page\" value=\"1\">\n<input type=\"hidden\" name=\"tab\" value=\"all\">\n<span id=\"u30searchw3title\" class=\"u30visually-hidden\">Search Oracle.com</span>\n<div id=\"u30searchw3\" data-pagestitle=\"SUGGESTED LINKS\" data-autosuggesttitle=\"SUGGESTED SEARCHES\" data-allresultstxt=\"All results for\" data-allsearchpath=\"https://search.oracle.com/results?q=u30searchterm&size=10&page=1&tab=all\" role=\"listbox\" aria-labelledby=\"u30searchw3title\">\n<ul id=\"u30quicklinks\" class=\"autocomplete-items\" role=\"group\" aria-labelledby=\"u30quicklinks-title\">\n<li role=\"presentation\" class=\"u30auto-title\" id=\"u30quicklinks-title\">QUICK LINKS</li>\n<li role=\"option\"><a href=\"/cloud/\" data-lbl=\"quick-links:oci\">Oracle Cloud Infrastructure</a>\n</li>\n<li role=\" option\"><a href=\"/applications/\" data-lbl=\"quick-links:applications\">Oracle Fusion Cloud Applications</a></li>\n<li role=\"option\"><a href=\"/database/technologies/\" data-lbl=\"quick-links:database\">Oracle Database</a></li>\n<li role=\"option\"><a href=\"/java/technologies/downloads/\" data-lbl=\"quick-links:download-java\">Download Java</a>\n</li>\n<li role=\"option\"><a href=\"/careers/\" data-lbl=\"quick-links:careers\">Careers at Oracle</a></li>\n</ul>\n</div>\n<span class=\"u30submit\">\n<input class=\"u30searchbttn\" type=\"submit\" value=\"Submit Search\">\n</span>\n<button id=\"u30clear\" type=\"reset\" aria-label=\"Clear Search\">\n<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" aria-hidden=\"true\" fill=\"none\"\nxmlns=\"http://www.w3.org/2000/svg\">\n<path\nd=\"M7 7L13 13M7 13L13 7M19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C14.9706 1 19 5.02944 19 10Z\"\nstroke=\"#161513\" stroke-width=\"2\" />\n</svg>\n</button>\n</span>\n</div>\n</form>\n<div id=\"u30announce\" data-alerttxt=\"suggestions found to navigate use up and down arrows\"\nclass=\"u30visually-hidden\" aria-live=\"polite\"></div>\n</div>\n</div>\n<button class=\"u30searchBtn\" id=\"u30searchBtn\" type=\"button\" aria-label=\"Open Search Field\"\naria-expanded=\"false\">\n<span>Search</span>\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\">\n<path d=\"M15,15l6,6M17,10a7,7,0,1,1-7-7A7,7,0,0,1,17,10Z\" />\n</svg>\n</button>\n</div>\n<span id=\"ac-flag\">\n<a class=\"flag-focus\" href=\"/countries-list.html#countries\" data-ajax=\"true\" rel=\"lightbox\" role=\"button\">\n<span class=\"sr-only\">Country</span>\n<img class=\"flag-image\" src=\"\" alt=\"\"/>\n<svg class=\"default-globe\" alt=\"Country\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<rect width=\"20\" height=\"20\" fill=\"url(#pattern0)\"/>\n<defs>\n<pattern id=\"pattern0\" patternContentUnits=\"objectBoundingBox\" width=\"1\" height=\"1\">\n<use xlink:href=\"#image0_1_1830\" transform=\"translate(-0.166667 -0.166667) scale(0.0138889)\"/>\n</pattern>\n<image alt=\"\" id=\"image0_1_1830\" width=\"96\" height=\"96\" xlink:href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIm0lEQVR4nO2dbYxdRRnHf9stu2i3gq/QotCtAawIolQUK6UVKL4BDVrjG4KJXzTSSMBgYiSkLQmagCG+JvpNo5WAEEKrUYioEUWkLk01Fm3XLwItZdvadpeme/fvh7lXb8s8c86duffMdvf8kpNN5jzPPHPmf865c+Zt+yRRk485uQsw26kFyEwtQGZqATJTC5CZWoDM1AJkphYgM7UAmakFyEwtQGbm5i5AAf3A+c3jLOBsYDEwD3hl8y/AIWBv8+8O4GlgO/AUMAI0qityZ/RNw86404APA5cClwAnJea3D/gN8AhwH/BMYn5dZboIcCKu0j+Nq/j+HsVpAL8CfgTcCxzuUZzS5BZgHvBZ4Eu4O79KdgPfBe4C/lNx7P+RS4C5wA3AV4BX5yhAG3uADcC3gcmqg+cQYCnuzltadeACtgKfB35fZdAqm6EDwN3An0ir/H9HniviPOB3uFfSQEI+HVGVAItxd9ZaoC8yj0lgPa4panF20yb2VdIH3IgTYlFkHp0hqdfHSkl7lcZ+Sava8rRonb+i6ZPCmKTl6nH99Po34Brgx8Bggd2LuKaojwO474G/tKVZhW5/ut4OPArMj4jZbvNx4IECu2h6+Qr6DHAP4crfB9wEPGecnwI+wdGVX5YtwKeaefh4Dri5WQaLE3HfC9dHxC9FrwS4Bvg+4Q+qh4AlwBD2+/a2pl0sDwLrjHOLcN8hS4DNgTz6gR8AqxPKYdOD99oKSROBd+uUpDskzZE0JOl5w26bpBOMGBY+27mSRgz7FyTNl9Qn6RZJjUDehyVdZsSIPrpd+YsV/sEdl3Rlm/2XDbuGpGWBOBaW/TLZlXtLm91VCt88Y5IWBeJkFWBA0hOBwh+UdGmb/aCkXYbtxoJYFiGfjYbPrmZZWnaXSzoUiPFH2U9mVgHuDhR6XC9t0q0xbBuSzimIZRHyOUf2U/CRY2xXKPwk3FkQq3IBlsm9231MSfqkx+fnhv1DJeJZFPltNvw2eWzXBK6pIemiEvEqESD0IydJ6zw+CyVNGvYfLBHTosjvSsNvUtIbPPbrA7G2yl17Uv1140PsRlz/yWxkLfDNlAxSBZgHjAKvTcnkOGYPMAwcjM0g9UPsc8zeygd4DW5AKZqUJ2AQ2AksTCnADOBZXG/vizHOKU/AGurKB1hAQjdFigDXJvjONKLrIlaAhbjZCz4uwXULW8dOw++iAr/2w6Ks/7sN/x0FfisMv1XAqYFymcQKsAZ/T+c/cKNJFi/H3/M5BWyLLEsM2/CPKQwDLwv4/Rb4pyd9Lm5aTcfECvBeI30j9mAJwJuMmDtJaMpFcAD/kzgHV0YLAT81zllvhCAxAvQDy41zoX51gDcb6U9FlCOVrUa6VcYW1jWuJGJCWYwAbwNO9qTvA54o8D3LSP97RDlSsWJaZWzxOLDfk34y8NZOCxEjgBXkzxRPgl1gpOeYr2nFLPoxbQBPGufO67QQMQJYd4hVqHasr+ZdEeVIxRqHPqWEr3WtoSkzXmIEsH6kni7ha13csxHlSMUS4HUlfK1rrUSARUb6aAlf6wnYHVGOVFIE2GGkD3daiJi+oH8BZ3TqNEsYxfULlSbmCbAmOtVE1E2MAEMRPrOFSgSo6SIxAlTZZXC8caBThxgBOg4yi+h4qVOMAL7PcHAddEXdwFbz7cwSvt3sju5rxvRR1CXdh90ZWYkAVnu/TBvYau+X+frsNla3SJmv8jca6dZYh0mMANuN9KJOLLAFiBrMSMQSvcxHofXFW6Y34Ci6KUCZdV/W3ZVDAGs8u8wTYF1rx726MQJYffdLKe4Pty7Oeh30Ekv0IgH6cV3yPqwxBpMYAUZw+zIcy0nAOwp8rUd0SUQ5UrEGXopeI+/Ev33CGBEDSzECNHBjoz4+UOD7VyO944GMLnCukf63Aj/rGh/FXg5lEvsl/IiR/jHCzcTt+As5TLVdHPPxt9qmCL/H+3DX6OPhmILECnAf/tGvM4GLA37juN5UXzmsO7IXnIv/RhkFJgJ+y/E3QSeB+2MKkjI18RfAFbHOM4xNwIdiHFM6436Y4DvTiK6LenJuOtkm5x5m9i7MaOdrRFY+1As0UtmNa02Nx2aQOiBzCLg9MY/jmXUkVD50Z0TsW7ivY4v1vLQ79zTsSVxXeey71R19teHXAE732G8IxNoCfC9wvhypq/yax4Wy1+B2ukx1c4l4FkV+VkzfMtWPKrxM9V0l4lWyTLV13BWomAm5xc/t9lUv1H6Lyi/UXqnwQu2vF8TKIsCApMcDhT4ktw1Ay366blWwSm5lv8VjmqZbFSC3kcVYoPATchtitOyr2qzjPSq3Wcdqhe/8PZJOD8TJLgByS/hDm11UvV3NQDMvH51sVzMu6WIjxrQSoHUnWVsRtNgkaYGkWwM2txr5W/hsbyvIf4HsH+cWkzr6yZ32AiDpehWLsFfSTZJGjfMN48ItjrW7WvZdPSrpZkn7Csp4RNJ1nry7cvR6077VwE8otzmeZXMQt/JyS1tamU37LsANkljjDGU27ZvA9f8/WGAXTa+nJj6A67L2DWG2E6qIIVxFvq+DuO8Hfk14kKeo8l/ALT/tWeUDlewbiqQz5HaaSuGIpA2S5gVshiTd3rRN4TF1ubVjHVXuHX0CcAfwRdKevGewu8BD58owBdyJ21T8SEI+pcmxefcFwHeAC6sOXMAIbvPuP1QZNMf09CdxWwWsBZ7PEP9YdgNfwM1rqrTyIf8/cBgErgO+Cry+4ti7gG/gdrxK6lJOIbcALQZxu+1ei2t59OpfmEwCv8SN4d5P/S9MvJzK0f/E51WJ+Y3hmrEPAz8jz5pkk+koQDtzcLPmzuf//8ZqGHgFbmuAVjv/IG6rhP24IdLWv7Eawc3X7HjGWlVMdwFmPPUivczUAmSmFiAztQCZqQXITC1AZmoBMlMLkJlagMzUAmSmFiAz/wUn9S8SexswyQAAAABJRU5ErkJggg==\"/>\n</defs>\n</svg>\n</a>\n</span>\n<div id=\"u30-profile\" class=\"u30-profile\">\n<button id=\"u30-flyout\" data-lbl=\"sign-in-account\" aria-expanded=\"false\">\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\"><path d=\"M17,19.51A3,3,0,0,0,14,17H10a3,3,0,0,0-3,2.51m9.92,0a9,9,0,1,0-9.92,0m9.92,0a9,9,0,0,1-9.92,0M15,10a3,3,0,1,1-3-3A3,3,0,0,1,15,10Z\"/></svg>\n<span class=\"acttxt\">View Accounts</span>\n</button>\n<div id=\"u30-profilew1\">\n<button id=\"u30actbck\" aria-label=\"close account flyout\"><span class=\"u30actbcktxt\">Back</span></button>\n<div class=\"u30-profilew2\">\n<span class=\"u30acttitle\">Cloud Account</span>\n<a href=\"/cloud/sign-in.html\" class=\" u30darkcta u30cloudbg\" data-lbl=\"sign-in-to-cloud\">Sign in to Cloud</a>\n<a href=\"/cloud/free/\" class=\"u30lightcta\" data-lbl=\"try-free-cloud-trial\">Sign Up for Free Cloud Tier</a>\n</div>\n<span class=\"u30acttitle\">Oracle Account</span>\n<ul class=\"u30l-out\">\n<li><a href=\"https://www.oracle.com/webapps/redirect/signon?nexturl=\" data-lbl=\"profile:sign-in-account\" class=\"u30darkcta\">Sign-In</a></li>\n<li><a href=\"https://profile.oracle.com/myprofile/account/create-account.jspx\" data-lbl=\"profile:create-account\" class=\"u30lightcta\">Create an Account</a></li>\n</ul>\n<ul class=\"u30l-in\">\n<li><a href=\"/corporate/contact/help.html\" data-lbl=\"help\">Help</a></li>\n<li><a href=\"javascript:sso_sign_out();\" id=\"u30pfile-sout\" data-lbl=\"signout\">Sign Out</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u30-contact\">\n<a href=\"/corporate/contact/\" data-lbl=\"contact-us\" title=\"Contact Sales\"><span>Contact Sales</span></a>\n</div>\n<a href=\"/global-menu-v2/\" id=\"u30ham\" role=\"button\" aria-expanded=\"false\" aria-haspopup=\"true\" aria-controls=\"u30navw1\">\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n<title>Menu</title>\n<path d=\"M16,4 L2,4\" class=\"p1\"></path>\n<path d=\"M2,12 L22,12\" class=\"p2\"></path>\n<path d=\"M2,20 L16,20\" class=\"p3\"></path>\n<path d=\"M0,12 L24,12\" class=\"p4\"></path>\n<path d=\"M0,12 L24,12\" class=\"p5\"></path>\n</svg>\n<span>Menu</span>\n</a>\n</div>\n</div>\n<script>\nvar u30min = [722,352,0,0];\nvar u30brsz = new ResizeObserver(u30 => {\ndocument.getElementById('u30').classList.remove('u30mobile','u30tablet','u30notitle');\nlet u30b = document.getElementById('u30brand').offsetWidth,\nu30t = document.getElementById('u30tools').offsetWidth ;\nu30v = document.getElementById('u30navw1');\nu30w = document.getElementById('u30w1').offsetWidth - 48;\nif((u30b + u30t) > u30min[0]){\nu30min[2] = ((u30b + u30t) > 974) ? u30b + u30t + 96 : u30b + u30t + 48;\n}else{\nu30min[2] = u30min[0];\n}\nu30min[3] = ((u30b + 112) > u30min[1]) ? u30min[3] = u30b + 112 : u30min[3] = u30min[1];\nu30v.classList.add('u30navchk');\nu30min[2] = (u30v.lastElementChild.getBoundingClientRect().right > u30min[2]) ? Math.ceil(u30v.lastElementChild.getBoundingClientRect().right) : u30min[2];\nu30v.classList.remove('u30navchk');\nif(u30w <= u30min[3]){\ndocument.getElementById('u30').classList.add('u30notitle');\n}\nif(u30w <= u30min[2]){\ndocument.getElementById('u30').classList.add('u30mobile');\n}\n});\nvar u30wrsz = new ResizeObserver(u30 => {\nlet u30n = document.getElementById('u30navw1').getBoundingClientRect().height,\nu30h = u30[0].contentRect.height;\nlet u30c = document.getElementById('u30').classList,\nu30w = document.getElementById('u30w1').offsetWidth - 48,\nu30m = 'u30mobile',\nu30t = 'u30tablet';\nif(u30c.contains('navexpand') && u30c.contains(u30m) && u30w > u30min[2]){\nif(!document.querySelector('button.u30navitem.active')){\ndocument.getElementById('u30ham').click();\n}\nu30c.remove(u30m);\nif(document.getElementById('u30').getBoundingClientRect().height > 80){\nu30c.add(u30t);\n}else{\nu30c.remove(u30t);\n}\n}else if(u30w <= u30min[2]){\nif(getComputedStyle(document.getElementById('u30tools')).visibility != \"hidden\"){\nu30c.add(u30m);\nu30c.remove(u30t);\n}\n}else if(u30n < 30 && u30h > 80){\nu30c.add(u30t);\nu30c.remove(u30m);\n}else if(u30n > 30){\nu30c.add(u30m);\nu30c.remove(u30t);\n}else if(u30n < 30 && u30h <= 80){\nu30c.remove(u30m);\nif(document.getElementById('u30').getBoundingClientRect().height > 80){\nu30c.add(u30t);\n}else{\nu30c.remove(u30t);\n}\n}\n});\nu30brsz.observe(document.getElementById('u30btitle'));\nu30wrsz.observe(document.getElementById('u30'));\n</script>\n</section>\n<!-- /U30v3 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- end : ocom/common/global/components/redwood/layoutAssetStartBodyInfo -->\n<!-- widgetType = PageTitleBannerV2 -->\n<!-- ptbWidget = true -->\n<!-- ptbWidget = true -->\n<!-- BEGIN:  oWidget_C/PageTitleBannerV2/Display -->\n<!-- RH03v2 -->\n<section class=\"rh03 rh03v4 rw-theme-150bg\" data-bgimg=\"/a/ocom/img/bgimg02-bgsw-overlay-01.png,/a/ocom/img/bgimg02-bgne-overlay-01.png\" data-trackas=\"rh03\" data-ocomid=\"rh03\">\n<div class=\"rh03w1 cwidth\">\n<!-- breadcrumb schema -->\n<!-- Display SEO Metadata -->\n<script type=\"application/ld+json\">\n{\n\"@context\": \"https://schema.org\",\n\"@type\": \"BreadcrumbList\",\n\"itemListElement\":\n[\n{ \"@type\": \"ListItem\", \"position\": 1, \"name\": \"IT Infrastructure\", \"item\": \"https://www.oracle.com/it-infrastructure/\" }\n]\n}\n</script>\n<!-- Widget - 1 -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<div class=\"herotitle\">\n<h1 class=\"rwaccent\">Oracle Hardware</h1>\n<p>Oracle hardware includes a full suite of scalable engineered systems, servers, and storage that let enterprises optimize application and database performance, protect crucial data, and lower costs. Organizations improve database performance, simplify management, and lower costs with exclusive features and automated operations that aren’t available with other solutions.</p>\n<div class=\"obttns\">\n<div>  <a class=\"icn-lock\" href=\"https://go.oracle.com/LP=147425\" data-cxdtrack=\":ow:evp:cpo::::Ocomcoverpage\" data-adbtrack=\"WWMK250117P00040:ow:evp:cpo::::Ocomcoverpage\" data-lbl=\"exadata-x11m-deep-dive:webinar\"><nobr>Watch the webinar</nobr><span class=\"sr-only\"> Exadata X11M Deep Dive</span></a></div>\n<div class=\"obttn1\"> <a href=\"/cloud/free/?source=:ow:o:p:nav:062520ITInfrastructureBC&amp;intcmp=:ow:o:p:nav:062520ITInfrastructureBC\" data-cxdtrack=\":ow:o:p:nav:062520ITInfrastructureBC\" data-adbtrack=\":ow:o:p:nav:062520ITInfrastructureBC\" data-lbl=\"bc-cta:try-oracle-cloud-free-tier\">Try Oracle Cloud Free Tier</a></div>\n</div>\n<div class=\"rh03vid clickvideo\">\n<div class=\"ytembed ytvideo ytthumbnail\" data-ytid=\"cngOjZkXTRA\" data-share=\"none\" data-autoplay=\"1\" aria-label=\"Introducing Exadata Public Cloud X11M video\"> <img src=\"/a/ocom/img/yt-video-thumbnail-cngOjZkXTRA.jpg\" alt=\"\"> </div>\n<div class=\"rh03txt\">\n<p class=\"rh03lnk\">Introducing Exadata X11M (17:12)</p>\n</div>\n</div>\n</div>\n<!-- END: oWidget_C/_Raw-Text/Display -->\n</div>\n</section>\n<!-- /RH03v2 -->\n<!-- END:  oWidget_C/PageTitleBannerV2/Display -->\n<!-- start : ocom/common/global/components/compass/related-widgets -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC59v5 -->\n<section class=\"rc59 rc59v5 rw-neutral-20bg rc59dark\" data-trackas=\"rc59\" data-ocomid=\"rc59\" data-a11y=\"true\">\n<div class=\"rc59w1 cwidth\">\n<ul class=\"rc59w2\">\n<li class=\"rc59w3\"> <img class=\"rc59img\" data-src=\"/a/ocom/img/rc59alt-exadata-db-on-exascale-infrastructure.png\" alt=\"\"> <strong><a href=\"/news/announcement/oracle-helps-customers-achieve-extreme-availability-and-performance-for-mission-critical-and-agentic-ai-applications-2025-08-07\" data-lbl=\"read-press-globally-distributed-exadata-title\">Announcing Oracle Globally Distributed Exadata Database on Exascale Infrastructure</a></strong>\n<p>Automatically distribute Oracle databases across nodes, regions, and countries to help meet data residency, extreme availability, and performance needs.</p>\n<div class=\"obttns\">\n<div class=\"obttn2\"> <a href=\"/news/announcement/oracle-helps-customers-achieve-extreme-availability-and-performance-for-mission-critical-and-agentic-ai-applications-2025-08-07\" data-lbl=\"read-press-globally-distributed-exadata-cta\">Read the press release</a> </div>\n</div>\n</li>\n<li class=\"rc59w3\"> <img class=\"rc59img\" data-src=\"/a/ocom/img/rc59-globally-distribute-database.webp\" alt=\"\"> <strong><a href=\"https://go.oracle.com/LP=149586\" data-cxdtrack=\":ow:o:s:mt:::Hardware\" data-adbtrack=\"WWMK250722P00043:ow:o:s:mt:::Hardware\" data-lbl=\"register-oracle-exadata-title\">Deep-dive webinar on Oracle Globally Distributed Exadata Database on Exascale Infrastructure</a></strong>\n<p>Get an exclusive, in-depth look at this new Oracle Globally Distributed AI Database service.</p>\n<div class=\"obttns\">\n<div class=\"obttn2\"><a href=\"https://go.oracle.com/LP=149586\" data-cxdtrack=\":ow:o:s:mt:::Hardware\" data-adbtrack=\"WWMK250722P00043:ow:o:s:mt:::Hardware\" data-lbl=\"register-oracle-exadata-cta\">Access the webinar</a> </div>\n</div>\n</li>\n</ul>\n</div>\n</section>\n<!-- /RC59v5 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC11v1 --><!--\n<section class=\"rc11 rc11v1 rw-teal-40bg\" data-trackas=\"rc11\" data-ocomid=\"rc11\"  data-lbl=\"promo:\" data-a11y=\"true\">\n<div class=\"rc11w1 cwidth\">\n<div class=\"rc11w2 rc11bttns\">\n<div class=\"rc11w3\">\n<h2>Exadata—The innovation continues: Extreme scale, performance, and value</h2>\n<p>Watch Juan Loaiza, executive vice president of mission-critical database technologies at Oracle, and his special guest, Mark Papermaster, executive vice president and chief technology officer at AMD, discuss Oracle Exadata X10M innovations.</p>\n</div>\n<div class=\"obttns\">\n<div>\n<a href=\"?ytid=TzAXSuB4ubU&t\" rel=\"vbox\" data-trackas=\"rc28-lightbox\" data-lbl=\"watch-the-video\">Watch the video (24:20)</a>\n</div>\n</div>\n</div>\n</div>\n</section>\n--><!-- /RC11v1 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC59v0 -->\n<section class=\"rc59 rc59v0\" data-trackas=\"rc59\" data-ocomid=\"rc59\" data-lbl=\"it-infra:cards\"  data-a11y=\"true\">\n<div class=\"rc59w1 cwidth\">\n<div class=\"rc59w2\">\n<div class=\"rc59w3\">\n<strong>Modernize Oracle AI Database environments</strong>\n<p>Find out how telcos are modernizing mission-critical systems to improve operational efficiency.</p>\n<div class=\"obttns\">\n<div class=\"obttn2\">\n<a href=\"/a/ocom/docs/engineered-systems/omdia-oracle-exadata-ebook-for-telco.pdf\" data-lbl=\"card-1:read-omdia-s-ebook\">Read OMDIA’s ebook (PDF)</a>\n</div>\n</div>\n</div>\n<div class=\"rc59w3\">\n<strong>Accelerate infrastructure deployment with Oracle Private Cloud Appliance</strong>\n<p>Understand how to use appliances to accelerate innovation.</p>\n<div class=\"obttns\">\n<div class=\"obttn2\">\n<a href=\"/a/ocom/docs/esg-wp-oracle-pca-june-2022.pdf\" data-lbl=\"card-2:read-esg-s-analysis\">Read ESG’s analysis (PDF)</a>\n</div>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC59v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC13v0 -->\n<section class=\"rc13 rc13v0 cpad rw-neutral-10bg rw-pattern4 rw-pattern-20p\" data-trackas=\"rc13\" data-ocomid=\"rc13\" data-lbl=\"differentiator:hardware\" data-a11y=\"true\">\n<div class=\"rc13w1 cwidth\">\n<div class=\"rc13w2\">\n<div class=\"rc13w3\">\n<h3 class=\"rwaccent-2\">Oracle hardware benefits</h3>\n</div>\n</div>\n<div class=\"rc13w5 col-framework col-justified col4 col-gutters\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<div class=\"col-item-w1\">\n<h4>Oracle AI Database runs faster and more efficiently</h4>\n<p>Oracle hardware lets IT teams deploy optimized solutions in customer data centers and easy-to-manage appliances in remote offices and edge environments. Solutions with full-stack integration and optimizations let customers run Oracle AI Database up to 10X faster with Oracle Exadata’s Smart Scan query offload.</p>\n<a href=\"?ytid=NHilWEUCINw\" rel=\"vbox\" data-trackas=\"rc13-lightbox\"  role=\"button\" aria-label=\"NTT DOCOMO video\">Watch NTT DOCOMO’s story (0:48)</a>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1\">\n<h4>Easy integration with Oracle’s public cloud</h4>\n<p>Built-in Oracle Cloud Infrastructure (OCI) integrations make it easy for organizations to develop and deploy cloud native applications across OCI and customer data centers, store data in the public cloud, and simplify both data protection and cloud migrations. Oracle Cloud Observability and Management Platform provides IT teams with the information they need to understand and manage all the layers of their IT stack, no matter where it’s located.</p>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1\">\n<h4>Enhanced availability and data protection</h4>\n<p>Oracle IT infrastructure helps organizations increase the availability of their mission-critical Oracle databases and applications. Fault-tolerant, scale-out designs and built-in capabilities simplify the implementation of Oracle Maximum Availability Architecture (MAA) best practices, letting customers achieve up to 99.999% availability with Oracle Exadata in MAA configurations, as described in <a href=\"/a/ocom/docs/database/idc-al4-servers.pdf\" target=\"_blank\">IDC’s analysis (PDF).</a></p>\n<a href=\"?ytid=W5YAcoN1aiI\" rel=\"vbox\" data-lbl=\"watch-exelon-story\" data-trackas=\"lightbox\">Watch Exelon’s story (1:28)</a><br />\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1\">\n<h4>Lower costs with high performance consolidation</h4>\n<p>Oracle hardware lowers the cost of running on-premises workloads by reducing the number of systems required, delivering higher performance per software license, and reducing IT staff workloads. Oracle Private Cloud Appliance lets customers consolidate containerized, cloud native applications—and those running natively on multiple operating systems—onto a single platform while <a href=\"/cloud/cloud-at-customer/\">Cloud@Customer</a> solutions let organizations benefit from consumption-based economics in their own data centers. </p>\n<a href=\"/a/ocom/docs/wikibon-cloud-database-battle.pdf\" target=\"_blank\">Analyst report: Autonomous AI Database on Exadata Cloud@Customer reduces TCO by 47% (PDF)</a>\n<br />\n<br />\n<a href=\"?ytid=QTdEEjG2tmk\" rel=\"vbox\" data-lbl=\"watch-the-halliburton-story\" data-trackas=\"lightbox\">Watch Hyundai Home Shopping Group's story (1:48)</a>\n</div>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC13v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC30v1 Products -->\n<section class=\"rc30 rc30v1 cpad rw-theme-130bg rw-pattern-theme1w rw-pattern-8p\" data-trackas=\"rc30\" data-ocomid=\"rc30\" data-lbl=\"products:hardware\"  data-a11y=\"true\">\n<div class=\"rc30w1 cwidth\">\n<h2 class=\"rwaccent\">Oracle hardware products</h2>\n<div class=\"rc30w2\">\n<form class=\"oform rc30mobnav\">\n<span class=\"oform-w1\">\n<span class=\"oselect\">\n<select name=\"f4\">\n<option value=\"#exadata\">Oracle Exadata</option>\n<option value=\"#engineered-systems\">Oracle Engineered Systems</option>\n<option value=\"#servers\">Oracle servers</option>\n<option value=\"#storage\">Oracle storage</option>\n</select>\n</span>\n</span>\n</form>\n</div>\n<div class=\"rc30w3\">\n<div class=\"rc30w4 rc30pane rc30pane-bleed rw-neutral-30bg\" id=\"exadata\" data-lbl=\"feature-1-label:oracle-exadata\">\n<div class=\"col-framework col2\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w10\">\n<div class=\"rc30w5\">\n<h3>The best Oracle AI Database platform</h3>\n<p>Oracle Exadata's full-stack architecture improves the performance, scale, and availability of an enterprise’s Oracle databases. Exadata gives you choice and flexibility in terms of where to run your Oracle AI Database workloads, with availability on Oracle Cloud Infrastructure (OCI); multicloud environments with AWS, Azure, and Google Cloud; a hybrid cloud in your data center; and with on-premises deployments.</p>\n<div class=\"rc30cta\">\n<div class=\"obttns\">\n<div>\n<a href=\"/engineered-systems/exadata/\" data-lbl=\"product-1-oracle-exadata:explore-exadata\">Explore Oracle Exadata</a>\n</div>\n</div>\n</div>\n<h5>Features</h5>\n<ul class=\"odlist\">\n<li>A full-stack, integrated solution reduces integration requirements.\n<li>A high-availability infrastructure maximizes database uptime.</li>\n<li>A scalable design enables database consolidation for organizations of any size.</li>\n<li>Full compatibility across on-premises, hybrid cloud, OCI, and multicloud deployments simplifies moving to the cloud.</li>\n<li>Intelligent storage increases database performance and controls costs.</li>\n<li>SQL read latency of less than 14 microseconds improves OLTP responsiveness.</li>\n<li>Up to 7 TB/sec SQL throughput accelerates data-intensive analytics applications.</li>\n<li>Up to 22.4 million SQL I/O operations per second per rack increases throughput for consolidated workloads.</li>\n</ul>\n</div>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w11\">\n<div data-bgimg=\"/a/ocom/img/rc30v1-hardware-product-exadata.png\" alt=\"Oracle Exadata Database Machine\"><br /></div>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"rc30w4 rc30pane rc30pane-bleed rw-neutral-30bg\" id=\"engineered-systems\" data-lbl=\"feature-2-label:oracle-engineered-systems\">\n<div class=\"col-framework col2\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w10\">\n<div class=\"rc30w5\">\n<h3>Pre-built and optimized for crucial enterprise workloads</h3>\n<p>Oracle Engineered Systems are integrated, full-stack solutions that are developed with Oracle AI Database and applications to run customer workloads faster, at lower costs, and with greater security than multivendor, on-premises solutions. Scalable designs enable  enterprises to consolidate  existing IT infrastructure and quickly adjust to surges in demand, while management automation reduces administrative workloads and helps control costs.</p>\n<div class=\"rc30cta\">\n<div class=\"obttns\">\n<div>\n<a href=\"/engineered-systems/\" data-lbl=\"product-2-engineered-systems-explore-engineered-systems\">Explore Engineered Systems</a>\n</div>\n</div>\n</div>\n<h5>Features</h5>\n<ul class=\"odlist\">\n<li>Complete solutions eliminate  multivendor infrastructure integration</li>\n<li>Full-stack quarterly patching <a href=\"/a/ocom/docs/engineered-systems/exadata/dsc-on-x8m-openworld.pdf\" data-lbl=\"product-2-engineered-systems:over-80-percent\">cuts patching workload by over 80% (PDF)</a></li>\n<li>Reliable edge appliances reduce downtime in remote locations by up to up to 99% as described by IDC</li>\n<li>Cloud data-protection and migration capabilities make it easy to protect data and move to the cloud</li>\n<li>Incremental scaling easily supports growing application and database requirements</li>\n<!-- <li>Trusted partition licensing <a href=\"//www.oracle.com/customers/ica-1-private-cloud.html\" data-lbl=\"product-2-engineered-systems:cuts-software-costs-by-up-to-85-percent\">cuts software costs by up to 85%</a></li>  -->\n<li>Trusted partition licensing cuts software costs by up to 85%</li>\n<li>Continuous protection for crucial Oracle databases</li>\n</ul>\n</div>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w11\">\n<div data-bgimg=\"/a/ocom/img/rc30v1-hardware-product-engineeredsys.png\" alt=\"Oracle Engineered Systems\"><br /></div>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"rc30w4 rc30pane rc30pane-bleed rw-neutral-30bg\" id=\"servers\" data-lbl=\"feature-3-label:oracle-servers\">\n<div class=\"col-framework col2\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w10\">\n<div class=\"rc30w5\">\n<h3>Secure application infrastructure</h3>\n<p>Oracle’s x86 and SPARC servers run enterprise applications in on-premises data centers and edge environments with high performance, security, reliability, and efficiency. Enterprise Java, Oracle AI Database, and application workloads run at peak performance and with efficient virtualization while the inclusion of operating system and virtualization software at no addition charge lower total cost of ownership (TCO).</p>\n<div class=\"rc30cta\">\n<div class=\"obttns\">\n<div>\n<a href=\"/servers/\" data-lbl=\"product-3-oracle-servers:explore-servers\">Explore Servers</a>\n</div>\n<div class=\"obttn1\">\n<a href=\"/a/ocom/docs/esg-oracle-x86-analysis.pdf\" data-lbl=\"product-3-servers:x86-innovations-pdf\">Oracle x86 innovations (PDF)</a>\n</div>\n</div>\n</div>\n<h5>Features</h5>\n<ul class=\"odlist\">\n<li>Entry-level to high-end servers for scale-up, scale-out, and edge deployments</li>\n<li>SPARC servers meet enterprise needs for reliable and cost-effective UNIX solutions</li>\n<li>Oracle Linux or Oracle Solaris and Oracle VM are included at no cost with every Oracle x86 server</li>\n<li>Proven Solaris operating system guaranteed to run existing SPARC/Solaris applications into the future</li>\n<li>Redundant designs with hot-swappable components increase Oracle AI Database and application availability</li>\n<li>Integrated encryption, application memory protection, and compliance auditing improve data and system security</li>\n<li>Oracle Solaris and multiple forms of virtualization are included at no cost with every Oracle SPARC server</li>\n</ul>\n</div>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w11\">\n<div data-bgimg=\"/a/ocom/img/rc30v1-hardware-product-servers.png\" alt=\"Oracle Servers\"><br /></div>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"rc30w4 rc30pane rc30pane-bleed rw-neutral-30bg\" id=\"storage\" data-lbl=\"feature-4-label:oracle-storage\">\n<div class=\"col-framework col2\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w10\">\n<div class=\"rc30w5\">\n<h3>High performance storage</h3>\n<p>Oracle’s high performance enterprise storage is optimized for Oracle workloads and cloud with unmatched TCO for active storage, data protection, and archive. Leading, large-scale enterprises continue to choose Oracle storage to run their applications faster, provides superior protection against cyberattacks, and securely preserve their long-term data. Unique integration with the Oracle AI Database and Oracle Cloud Infrastructure provide unmatched efficiency, simplicity, and performance.</p>\n<div class=\"rc30cta\">\n<div class=\"obttns\">\n<div class=\"obttn\">\n<a href=\"/storage/\" data-lbl=\"product-4-storage-explore-storage\">Explore Storage</a>\n</div>\n<div class=\"obttn1\">\n<a href=\"/a/ocom/docs/esg-oracle-x86-analysis.pdf\" data-lbl=\"product-4-storage-inno:read-esg-s-analysis-pdf\">Read ESG’s analysis  (PDF)</a>\n</div>\n</div>\n</div>\n<h5>Features</h5>\n<ul class=\"odlist\">\n<li>Unified file, block, and object storage meets customers active storage needs</li>\n<li>Up to 8 PB of all-flash storage for latency-sensitive applications</li>\n<li>Up to 18 GB/Sec throughput accelerates data-warehouse and data-protection workloads</li>\n<li>IO prioritization optimizes Oracle AI Database storage performance</li>\n<li>Data protection solutions with unique Oracle AI Database integrations accelerates recovery of crucial information</li>\n<li>Tape automation <a href=\"/a/ocom/docs/storage/tape-storage/esg-oracle-tape-economics-2934208.pdf\" data-lbl=\"product-4-storage:reduces-the-cost-of-managing-archives-by-up-to-95-percent\">reduces the cost of managing archives by up to 95% (PDF)</a> according to ESG</li>\n<li>Up to 57 EB of uncompressed capacity and 29 PB/hour throughput under a single pane of glass management provides nearly limitless scalability</li>\n</ul>\n</div>\n</div>\n</div>\n<div class=\"col-item\">\n<div class=\"col-item-w1 rc30w11\">\n<div data-bgimg=\"/a/ocom/img/rc30v1-hardware-product-storage.png?foo4\" alt=\"Oracle Storage\"><br /></div>\n</div>\n</div>\n</div>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC30v1 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC28v0 -->\n<section class=\"rc28 rc28v0 rw-neutral-150bg cpad cmp-overlap\" data-ocomid=\"rc28\" data-trackas=\"rc28\" data-lbl=\"customers:hardware\"  data-a11y=\"true\">\n<div class=\"rc28w1 cwidth\">\n<strong><a href=\"https://www.oracle.com/customers/\" data-lbl=\"view-all:customer-successes\">View all customer successes</a></strong>\n<h2 class=\"rw-ptitle\">Oracle hardware customer successes</h2>\n<p class=\"rw-intro\">Thousands of organizations worldwide use Oracle hardware to run and protect business-critical databases and applications with high performance, scale, and security while reducing administrative burdens and TCO.</p>\n<div class=\"col-framework col4 col-2upmobile col-multi col-gutters\">\n<div class=\"col-w1\">\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-equinix.png\" alt=\"Equinix logo\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-kcb.png\" alt=\"KCB logo\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-exelon.png\" alt=\"Exelon\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-icbc.png\" alt=\"ICBC\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-tractorsupply-logo.png\" alt=\"Tractor Supply\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-halliburton.png\" alt=\"Halliburton\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-toyo.png\" alt=\"Toyo Engineering\">\n</div>\n<div class=\"col-item\">\n<img data-src=\"/a/ocom/img/customerlogo-evergy.svg\" alt=\"Evergy logo\">\n</div>\n</div>\n</div>\n<div class=\"rc28w2 rw-neutral-180bg rw-pattern6w rw-pattern-20p\">\n<div class=\"rc28w3\" data-bgimg=\"/a/ocom/img/rc28-hardware-customersuccessr.png\" alt=\"Swisscom video image\"></div>\n<div class=\"rc28w4\">\n<img data-src=\"/a/ocom/img/customerlogo-swisscom.svg\" alt=\"Swisscom logo\">\n<h3>Swisscom improves operational efficiency and protects databases with Oracle hardware</h3>\n<div class=\"obttns\">\n<div><a href=\"?ytid=bpEk1O6oKdQ\" rel=\"vbox\" data-lbl=\"view-customer:watch-swisscom-s-exadata-story\" data-trackas=\"lightbox\">Watch Swisscom’s Exadata story (1:35)</a>\n</div>\n<div class=\"obttn1\">\n<a href=\"?ytid=TtFGXgdZzRk\" rel=\"vbox\" data-lbl=\"view-customer:watch-swisscom-s-recovery-appliance-story\" data-trackas=\"lightbox\">Watch Swisscom’s Recovery Appliance story (1:31)</a>\n</div>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC28v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC36v0 -->\n<section class=\"rc36 rc36v0 cpad rw-neutral-10bg rw-pattern6 rw-pattern-10p\" data-ocomid=\"rc36\" data-trackas=\"rc36\" data-lbl=\"use-cases:hardware\" data-a11y=\"true\">\n<div class=\"rc36w1 cwidth\">\n<h2>Oracle hardware use cases</h2>\n<div class=\"rc36w2\">\n<ul>\n<li>\n<h3>Simplify management with cloud capabilities</h3>\n<p>Accelerate enterprise applications and databases while reducing IT management workloads with the same database and systems technology as Oracle Cloud Infrastructure.</p>\n<p><a href=\"/news/announcement/oracle-and-deutsche-bank-2021-06-24/\" data-lbl=\"read-the-deutsche-bank-story\">Read the Deutsche Bank story</a></p></li>\n<li>\n<h3>Implement an Oracle AI Database private cloud</h3>\n<p>Reduce data center complexity by consolidating multiple database workloads on the same Exadata Cloud@Customer infrastructure.</p>\n<p><a href=\"/customers/moneta-money-bank/\" data-lbl=\"read-the-moneta-s-story\">Read the MONETA Money Bank story</a></p></li>\n<li>\n<h3>Consolidate application infrastructure</h3>\n<p>Reduce the number of servers and storage systems required to run application workloads, cut IT management workloads with automation, and lower total cost of ownership by consolidating all aspects of your IT environment with Oracle hardware.</p>\n<p><a href=\"/customers/zonamerica-colombia/\" data-lbl=\"read-zonamerica-story\">Read Zonamerica’s story</a></p></li>\n<li>\n<h3>Modernize applications with optimized in-database analytics and machine learning</h3>\n<p>Integrate Oracle Machine Learning capabilities into applications, build machine-learning models faster by eliminating data movement, and accelerate business analytics with Oracle Exadata’s optimized in-database capabilities.</p>\n<p><a href=\"https://partners.wsj.com/oracle/yapi-kredi-drives-mobile-banking-innovation-with-big-data-analytics/\"data-lbl=\"read-the-yapi-kredi-bank-story\" target=\"_blank\">Read Yapi Kredi Bank’s story</a></p></li>\n<li>\n<h3>Protect application and database information enterprisewide</h3>\n<p>Protect databases with up to 50X faster backups and 8X faster recoveries while speeding up the protection of all other data with high performance Oracle hardware.</p>\n<p><a href=\"?ytid=8tixesHJVJU\" rel=\"vbox\" data-lbl=\"watch-the-energy-transfer story\" data-trackas=\"lightbox\">Watch Energy Transfer’s story (1:24)</a></p></li>\n</ul>\n</div>\n</div>\n</section>\n<!-- /RC36v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC29v0 -->\n<section class=\"rc29 rc29v0\" data-trackas=\"rc29\" data-lbl=\"blog:hardware\" data-a11y=\"true\">\n<div class=\"rc29w1\">\n<div class=\"col-framework col2\">\n<div class=\"col-w1\">\n<div class=\"col-item cpad cwidth rw-neutral-10bg\" data-bgimg=\"/a/ocom/img/bgimg02-bgsw-blogs-01.png\">\n<strong class=\"rc29sub\">January 7, 2025</strong>\n<h3> Introducing Oracle Exadata X11M for Exadata Database Service and Autonomous AI Database</h3>\n<p>Bob Thome, Vice President, Exadata Product Management</p>\n<p>We are excited to announce the immediate availability of Oracle Exadata X11M for cloud deployments. This next-generation intelligent data platform runs Oracle Exadata Database Service and Oracle Autonomous AI Database in the Oracle Cloud, multicloud deployments, and in your data center with Exadata Cloud@Customer.</p>\n<div class=\"rc29lnk\"><a  href=\"https://blogs.oracle.com/database/post/introducing-exadata-x11m-for-cloud\" data-lbl=\"featured-blog:read-the-complete-post\">Read the complete post</a>  | <a href=\"https://go.oracle.com/LP=25600?elqCampaignId=109966&bn=inf\">Subscribe to the blog</a>\n</div>\n</div>\n<div class=\"col-item cwidth cpad cwidth rw-neutral-20bg\" data-bgimg=\"/a/ocom/img/bgimg02-bgne-blogs-02.png\">\n<h4>Featured Oracle hardware blogs</h4>\n<ul>\n<li>\n<strong>January 7, 2025</strong>\n<a href=\"https://blogs.oracle.com/exadata/post/exadata-x11m\" data-lbl=\"blog:introducing-exadata-x11m\">Introducing Exadata X11M: Next-Generation Intelligent Data Architecture</a>\n</li>\n<li>\n<strong>January 7, 2025</strong>\n<a href=\"https://blogs.oracle.com/infrastructure/post/global-industry-analyst-perspectives-on-oracle-exadata-x11m\" data-lbl=\"blog:global-industry-analyst-perspectives-on-oracle-exadata-x11m\">Global Industry Analyst Perspectives on Oracle Exadata X11M</a>\n</li>\n<li>\n<strong>MARCH 7, 2023</strong>\n<a href=\"https://blogs.oracle.com/infrastructure/post/york-university-chooses-oracle-exadata-cloudcustomer-to-improve-the-experience-of-students-educators-and-staff\" data-lbl=\"blog:york-university-chooses-oracle-exadata-cloudcustomer\">York University chooses Oracle Exadata Cloud@Customer to improve the experience of students, educators, and staff</a>\n</li>\n<li>\n<strong>MAY 4, 2022</strong>\n<a href=\"https://blogs.oracle.com/infrastructure/post/oracle-exadata-cloud-at-customer-enhances-moneta-move-to-digital-banking\" data-lbl=\"blog:oracle-exadata-cloud-at-customer-enhances-moneta\">Oracle Exadata Cloud@Customer enhances MONETA’s move to digital banking</a>\n</li>\n<li>\n<strong>MARCH 11, 2022</strong>\n<a href=\"https://blogs.oracle.com/infrastructure/post/global-industry-analyst-perspectives-multi-vm-autonomous-database-on-exadata-cloudcustomer\" data-lbl=\"blog:global-industry-analyst-perspectives-multi-vm\">Global industry analyst perspectives on multi-VM Autonomous Database on Exadata Cloud@Customer</a>\n</li>\n</ul>\n<a class=\"rc29lnk\" href=\"//blogs.oracle.com/infrastructure/\" data-lbl=\"blog-view-all:hardware\">View all</a>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC29v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- RC31v0 -->\n<section class=\"rc31 rc31v0 cpad rw-neutral-10bg rw-pattern13 rw-pattern-20p\" data-ocomid=\"rc31\" data-trackas=\"rc31\">\n<div class=\"rc31w1 cwidth\">\n<h2>Get started</h2>\n<div class=\"col-framework col-gutters\">\n<div class=\"col-w1 rc31alt\">\n<div class=\"col-item rc31one\">\n<div class=\"col-item-w1 rw-yellow-40bg\">\n<div class=\"icn-img icn-contact\"><br /></div>\n<h3>Contact an Oracle expert</h3>\n</div>\n<div class=\"col-item-w2\">\n<p>Find out how Oracle hardware solutions can help your specific business needs.</p>\n<div class=\"obttns\">\n<div class=\"obttn1\">\n<a href=\"/it-infrastructure/hardware-category-contact-form.html\" rel=\"lightbox\" data-width=\"640\" title=\"Contact Oracle Hardware\" data-lbl=\"lightbox-open-contact-oracle-hardware\" data-trackas=\"rc31-lightbox\">Get in touch</a>\n</div>\n</div>\n</div>\n</div>\n</div>\n</div>\n</div>\n</section>\n<!-- /RC31v0 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- end : ocom/common/global/components/compass/related-widgets -->\n<!-- start : ocom/common/global/components/framework/layoutAssetEndBodyInfo -->\n<!-- Dispatcher:null -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- U10v0 -->\n<footer id=\"u10\" class=\"u10 u10v6\" data-trackas=\"footer\" type=\"redwood\" data-ocomid=\"u10\" data-a11y=\"true\">\n<nav class=\"u10w1\" aria-label=\"Footer\">\n<div class=\"u10w2\">\n<div class=\"u10w3\">\n<div class=\"u10ttl\">Resources for</div>\n<ul>\n<li><a data-lbl=\"resources-for:career\" href=\"/careers/\">Careers</a></li>\n<li><a data-lbl=\"resources-for:developers\" href=\"/developer/\">Developers</a></li>\n<li><a data-lbl=\"resources-for:investors\"\nhref=\"https://investor.oracle.com/home/default.aspx\">Investors</a></li>\n<li><a data-lbl=\"resources-for:partners\" href=\"/partner/\">Partners</a></li>\n<li><a data-lbl=\"resources-for:researchers\" href=\"/research/\">Researchers</a></li>\n<li><a data-lbl=\"resources-for:students-educators\"\nhref=\"https://academy.oracle.com/en/oa-web-overview.html\">Students and Educators</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u10w2\">\n<div class=\"u10w3\">\n<div class=\"u10ttl\">Why Oracle</div>\n<ul>\n<li><a data-lbl=\"why-oracle:analyst-reports\" href=\"/corporate/analyst-reports/\">Analyst Reports</a>\n</li>\n<li><a data-lbl=\"why-oracle:gartner-mq-erp-cloud\" href=\"/erp/what-is-erp/best-erp/\">Best cloud-based\nERP</a></li>\n<li><a data-lbl=\"why-oracle:cloud-economics\" href=\"/cloud/economics/\">Cloud Economics</a></li>\n<li><a data-lbl=\"why-oracle:corporate-responsibility\" href=\"/social-impact/\">Social Impact</a></li>\n<li><a data-lbl=\"why-oracle:security-pracitices\" href=\"/corporate/security-practices/\">Security Practices</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u10w2\">\n<div class=\"u10w3\">\n<div class=\"u10ttl\">Learn</div>\n<ul>\n<li><a data-lbl=\"learn:sovreign-cloud\" href=\"/cloud/sovereign-cloud/what-is-sovereign-cloud/\">What is a sovereign cloud?</a></li>\n<li><a data-lbl=\"learn:zero-trust\" href=\"/security/what-is-zero-trust/\">What is zero trust security?</a></li>\n<li><a data-lbl=\"learn:ai-finance\" href=\"/erp/financials/ai-finance/\">How AI is transforming finance</a></li>\n<li><a data-lbl=\"learn:vector-db\"\nhref=\"/database/vector-database/\">What is a vector database?</a></li>\n<li><a data-lbl=\"learn:multicloud\" href=\"/cloud/multicloud/what-is-multicloud/\">What is multicloud?</a></li>\n<li><a data-lbl=\"learn:ai-agents\" href=\"/artificial-intelligence/ai-agents/\">What are AI agents?</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u10w2\">\n<div class=\"u10w3\">\n<div class=\"u10ttl\">News and Events</div>\n<ul>\n<li><a data-lbl=\"newsandevents:news\" href=\"/news/\">News</a></li>\n<li><a data-lbl=\"newsandevents:oracle-ai-world\" href=\"/ai-world/\">Oracle AI World</a></li>\n<li><a data-lbl=\"newsandevents:oracle-health-summit\" href=\"/health/health-summit/\">Oracle Health Summit</a></li>\n<li><a data-lbl=\"newsandevents:dev-tour\" href=\"/developer/dev-tour/\">Oracle Dev Tour</a></li>\n<li><a data-lbl=\"newsandevents:search-all-events\" href=\"https://search.oracle.com/events?q=&amp;lang=english\">Search all events</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u10w2\">\n<div class=\"u10w3\">\n<div class=\"u10ttl\">Contact Us</div>\n<ul>\n<li><a href=\"tel:18006330738\">US Sales: +1.800.633.0738</a></li>\n<li><a data-lbl=\"contact-us:how-can-we-help\" href=\"/corporate/contact/\">How can we help?</a></li>\n<li><a data-lbl=\"contact-us:subscribe-to-emails\" href=\"https://go.oracle.com/subscribe/?l_code=en&src1=OW:O:FO\">Subscribe to emails</a></li>\n<li><a data-lbl=\"contact-us:integrity-helpline\" href=\"https://secure.ethicspoint.com/domain/media/en/gui/31053/index.html\">Integrity Helpline</a></li>\n<li><a data-lbl=\"contact-us:accessibility\" href=\"/corporate/accessibility/\">Accessibility</a></li>\n</ul>\n</div>\n</div>\n<div class=\"u10w4\">\n<hr />\n</div>\n</nav>\n<div class=\"u10w11\">\n<ul class=\"u10-cr\">\n<li class=\"u10mtool\" id=\"u10cmenu\">\n<a data-lbl=\"country-region\" class=\"u10ticon u10regn\"\nhref=\"/universal-menu/#u10countrymenu\">Country/Region</a>\n</li>\n</ul>\n<nav class=\"u10w5 u10w10\" aria-label=\"Site info\">\n<ul class=\"u10-links\">\n<li></li>\n<li><a data-lbl=\"copyright\" href=\"/legal/copyright/\">&copy; 2025 Oracle</a></li>\n<li><a data-lbl=\"privacy\" href=\"/legal/privacy/\">Privacy</a><span>/</span><a\ndata-lbl=\"do-not-sell-my-info\" href=\"/legal/privacy/privacy-choices/\">Do Not Sell My\nInfo</a></li>\n<li>\n<div id=\"teconsent\"></div>\n</li>\n<li><a data-lbl=\"ad-choices\" href=\"/legal/privacy/privacy-policy/#adchoices\">Ad\nChoices</a></li>\n<li><a data-lbl=\"careers\" href=\"/corporate/careers/\">Careers</a></li>\n</ul>\n</nav>\n<nav class=\"u10scl\" aria-label=\"Social media links\">\n<ul class=\"scl-icons\">\n<li class=\"scl-facebook\"><a data-lbl=\"scl-icon:facebook\" href=\"https://www.facebook.com/Oracle/\" rel=\"noreferrer\" target=\"_blank\" title=\"Oracle on Facebook\"></a></li>\n<li class=\"scl-twitter\"><a data-lbl=\"scl-icon:twitter\" href=\"https://twitter.com/oracle\" rel=\"noreferrer\" target=\"_blank\" title=\"Follow Oracle on Twitter\"></a></li>\n<li class=\"scl-linkedin\"><a data-lbl=\"scl-icon:linkedin\" href=\"https://www.linkedin.com/company/oracle/\" rel=\"noreferrer\" target=\"_blank\" title=\"Oracle on LinkedIn\"></a></li>\n<li class=\"scl-youtube\"><a data-lbl=\"scl-icon:you-tube\" href=\"https://www.youtube.com/oracle/\" rel=\"noreferrer\" target=\"_blank\" title=\"Watch Oracle on YouTube\"></a></li>\n</ul>\n</nav>\n</div>\n</footer>\n<!-- /U10v6 -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n</div>\n<!-- Container/Display Start -->\n<!-- BEGIN: oWidget_C/_Raw-Text/Display -->\n<!-- Start - End Body Admin Container -->\n<!-- Start SiteCatalyst code -->\n<script type=\"text/javascript\" data-reqjq data-src=\"/asset/web/analytics/ora_ocom.js\"></script>\n<!-- End SiteCatalyst code -->\n<!-- End - End Body Admin Container -->\n<!-- END: oWidget_C/_Raw-Text/Display -->\n<!-- Container/Display end -->\n<!-- end : ocom/common/global/components/framework/layoutAssetEndBodyInfo -->\n</body>\n<!-- end : Framework/Redwood2 -->\n</html>\n\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/eclipse distribution license - v 1.0 - edl-v10.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\">\n    <!-- Google Tag Manager -->\n<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\nnew Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\nj=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=\n'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);\n})(window,document,'script','dataLayer','GTM-5WLCZXC');</script>\n<!-- End Google Tag Manager -->        <meta name=\"author\" content=\"Mike Milinkovich, January 25, 2008\"/>\n        <meta name=\"keywords\" content=\"edl, legal, license\"/>\n    <link rel=\"shortcut icon\" href=\"/eclipse.org-common/themes/solstice/public/images/favicon.ico\"/>\n    <title>Eclipse Distribution License | The Eclipse Foundation</title>\n    <link rel=\"preconnect stylesheet\" href=\"https://www.eclipse.org/public/css/styles.css?v=0.1.3\"/>\n<meta name=\"description\" content=\"The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks.\"/>\n<meta property=\"og:description\" content=\"The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks.\"/>\n<meta property=\"og:image\" content=\"https://www.eclipse.org/images/og-image.jpg\"/>\n<meta property=\"og:title\" content=\"Eclipse Distribution License | The Eclipse Foundation\"/>\n<meta property=\"og:image:width\" content=\"1200\"/>\n<meta property=\"og:image:height\" content=\"630\"/>\n<meta name=\"twitter:site\" content=\"@EclipseFdn\"/>\n<meta name=\"twitter:card\" content=\"summary\"/>\n<meta name=\"twitter:title\" content=\"Eclipse Distribution License | The Eclipse Foundation\"/>\n<meta name=\"twitter:url\" content=\"http://www.eclipse.org/org/documents/edl-v10.php\"/>\n<meta name=\"twitter:description\" content=\"The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 415 open source projects, including runtimes, tools and frameworks.\"/>\n<meta name=\"twitter:image\" content=\"https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-400x400.png\"/>\n<link href=\"https://fonts.googleapis.com/css2?family=Libre+Franklin:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap\" rel=\"preconnect stylesheet\" type=\"text/css\"/>\n    <script> var eclipse_org_common = {\"settings\":{\"cookies_class\":{\"name\":\"eclipse_settings\",\"enabled\":1}}}</script>  </head>\n  <body id=\"body_solstice\">\n    <!-- Google Tag Manager (noscript) -->\n<noscript><iframe src=\"https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC\"\nheight=\"0\" width=\"0\" style=\"display:none;visibility:hidden\"></iframe></noscript>\n<!-- End Google Tag Manager (noscript) -->    <a class=\"sr-only\" href=\"#content\">Skip to main content</a>\n    <div class=\"eclipsefdn-featured-story-banner\" data-publish-target=\"eclipse_org\"></div>\n<header class=\"header-wrapper\" id=\"header\">\n  <div class=\"navigation-bar dark\">\n    <div class=\"container\">\n      <div class=\"navigation-bar-inner\">\n        <div class=\"navigation-bar-left\">\n          <div class=\"wrapper-logo-default\"><a href=\"http://www.eclipse.org/\"><img class=\"logo-eclipse-default img-responsive navigation-bar-brand\" alt=\"Eclipse.org logo\" width=\"120\" src=\"//www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white-orange.svg\"/></a></div>        </div>\n        <nav class=\"navigation-bar-nav hidden-xs hidden-sm\">\n          <ul>\n                      <li>\n            <a href=\"#\" data-target=\"join-us-menu\" aria-expanded=\"false\">\n              Join us\n            </a>\n          </li>          <li>\n            <a href=\"#\" data-target=\"what-we-do-menu\" aria-expanded=\"false\">\n              What we do\n            </a>\n          </li>          <li>\n            <a href=\"#\" data-target=\"resources-menu\" aria-expanded=\"false\">\n              Resources\n            </a>\n          </li>          <li>\n            <a href=\"#\" data-target=\"about-us-menu\" aria-expanded=\"false\">\n              About us\n            </a>\n          </li>          </ul>\n        </nav>\n\n        <div class=\"navigation-bar-right\">\n          <div class=\"navigation-bar-controls hidden-xs hidden-sm\">\n            <ul>\n              <li class=\"dropdown\">\n                <a class=\"dropdown-toggle link-unstyled focus-highlight\" tabindex=\"0\" id=\"search-dropdown-toggle\" data-toggle=\"dropdown\" href=\"#\" aria-label=\"Search\">\n                  <i class=\"fa-solid fa-magnifying-glass\" aria-hidden=\"true\"></i>\n                </a>\n                <form class=\"search-bar dropdown-menu dropdown-menu-right\" aria-labelledby=\"search-dropdown-toggle\" action=\"https://www.eclipse.org/home/search/\" method=\"get\">\n                  <input class=\"search-bar-input\" name=\"q\" placeholder=\"Search\">\n                  <button class=\"btn btn-link\" type=\"submit\">\n                    <i class=\"fa-solid fa-arrow-right\"></i>\n                  </button>\n                </form>\n              </li>\n              <li class=\"dropdown\">\n                <a class=\"dropdown-toggle focus-highlight\" id=\"user-menu\" href=\"#\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\" tabindex=\"0\">\n                  <i class=\"fa-regular fa-user\" aria-hidden=\"true\"></i>\n                </a>\n                <ul class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"user-menu\">\n                  <li>\n                    <a href=\"https://accounts.eclipse.org/user\">\n                      <i class=\"fa-solid fa-user margin-right-10\" aria-hidden=\"true\"></i>\n                      View my account\n                    </a>\n                  </li>\n                  <li>\n                    <a href=\"https://accounts.eclipse.org/user/edit\">\n                      <i class=\"fa-solid fa-edit margin-right-10\" aria-hidden=\"true\"></i>\n                      Edit my account\n                    </a>\n                  </li>\n                  <li>\n                    <a role=\"link\" class=\"toolbar-manage-cookies\" href=\"#\" tabindex=\"0\">\n                      <i class=\"fa-solid fa-wrench margin-right-10\" aria-hidden=\"\"></i>\n                      Manage cookies\n                    </a>\n                  </li>\n                </ul>\n              </li>\n            </ul>\n          </div>\n          <div class=\"navigation-bar-ctas hidden-xs hidden-sm\">\n            <ul>\n              <li>\n                <div class=\"float-right hidden-xs\" id=\"btn-call-for-action\"><a href=\"/downloads/\" class=\"btn btn-outline-primary\">Download</a></div>              </li>\n            </ul>\n          </div>\n          <a class=\"mobile-menu-toggle visible-xs visible-sm\" aria-expanded=\"false\"></a>\n        </div>\n      </div>\n    </div>\n  </div>\n\n        <div class=\"mobile-menu hidden\">\n        <nav class=\"mobile-menu-nav\"><ul>        <li>\n          <a role=\"button\" href=\"#mobile-menu-join-us\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-join-us\">\n            Join us\n            <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n          </a>\n\n          <ul class=\"collapse\" id=\"mobile-menu-join-us\">\n                      <li>\n            <a role=\"button\" href=\"#mobile-menu-membership\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-membership\">Membership <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-membership\">\n              <li><a href=\"/membership/\" target=\"\">Become a member</a></li><li><a href=\"/membership/#tab-fees\" target=\"\">Review membership fees</a></li><li><a href=\"https://membership.eclipse.org/portal\" target=\"\">Access member portal</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-sponsorship\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-sponsorship\">Sponsorship <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-sponsorship\">\n              <li><a href=\"/sponsor/\" target=\"\">Become a sponsor</a></li><li><a href=\"/org/our-sponsors/\" target=\"\">Sponsor directory</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-contribute\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-contribute\">Contribute <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-contribute\">\n              <li><a href=\"/contribute/\" target=\"\">How to contribute</a></li><li><a href=\"/projects/training/\" target=\"\">Committer training</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-collaborate-with-us\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-collaborate-with-us\">Collaborate with us <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-collaborate-with-us\">\n              <li><a href=\"/org/working-groups/explore/\" target=\"\">Join an existing industry collaboration</a></li><li><a href=\"/collaborations/#start-collaboration\" target=\"\">Start a new collaboration</a></li>\n            </ul>\n          </li>\n          </ul>\n        </li>        <li>\n          <a role=\"button\" href=\"#mobile-menu-what-we-do\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-what-we-do\">\n            What we do\n            <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n          </a>\n\n          <ul class=\"collapse\" id=\"mobile-menu-what-we-do\">\n                      <li>\n            <a role=\"button\" href=\"#mobile-menu-projects\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-projects\">Projects <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-projects\">\n              <li><a href=\"https://projects.eclipse.org/\" target=\"\">Explore projects</a></li><li><a href=\"/specifications/\" target=\"\">View specifications</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-industry-collaborations\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-industry-collaborations\">Industry collaborations <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-industry-collaborations\">\n              <li><a href=\"/org/working-groups/explore/\" target=\"\">Explore collaborations</a></li><li><a href=\"/org/working-groups/about/\" target=\"\">Join a working group</a></li><li><a href=\"/collaborations/interest-groups/\" target=\"\">Join an interest group</a></li><li><a href=\"/org/value/\" target=\"\">Read success stories</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-core-services\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-core-services\">Core services <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-core-services\">\n              <li><a href=\"/org/services/\" target=\"\">Explore core services</a></li><li><a href=\"/security/\" target=\"\">Ensure project security</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-key-initiatives\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-key-initiatives\">Key initiatives <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-key-initiatives\">\n              <li><a href=\"/topics/edge-and-iot/\" target=\"\">Embedded &amp; IoT</a></li><li><a href=\"/topics/cloud-native/\" target=\"\">Enterprise Java</a></li><li><a href=\"https://dataspaces.eclipse.org/\" target=\"\">Data sovereignty</a></li><li><a href=\"/security/\" target=\"\">Security &amp; privacy</a></li><li><a href=\"/topics/ide/\" target=\"\">Developer tools &amp; IDEs</a></li><li><a href=\"https://aieclipse.org/\" target=\"\">AI &amp; emerging technologies <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li><li><a href=\"/topics/automotive-and-mobility/\" target=\"\">Automotive &amp; mobility</a></li><li><a href=\"https://orcwg.org/\" target=\"\">Policy &amp; compliance <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-strategic-services\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-strategic-services\">Strategic services <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-strategic-services\">\n              <li><a href=\"/research/\" target=\"\">Transform research into open source</a></li><li><a href=\"/os4biz/services/\" target=\"\">Engage professional services</a></li><li><a href=\"/os4biz/ospo/\" target=\"\">Build your OSPO</a></li>\n            </ul>\n          </li>\n          </ul>\n        </li>        <li>\n          <a role=\"button\" href=\"#mobile-menu-resources\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-resources\">\n            Resources\n            <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n          </a>\n\n          <ul class=\"collapse\" id=\"mobile-menu-resources\">\n                      <li>\n            <a role=\"button\" href=\"#mobile-menu-what's-happening\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-what's-happening\">What's happening <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-what's-happening\">\n              <li><a href=\"https://blogs.eclipse.org/\" target=\"\">Read our blog</a></li><li><a href=\"https://accounts.eclipse.org/mailing-list\" target=\"\">Join mailing lists</a></li><li><a href=\"https://newsroom.eclipse.org/\" target=\"\">Get news updates</a></li><li><a href=\"/community/eclipse_newsletter\" target=\"\">Subscribe to newsletter</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-events\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-events\">Events <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-events\">\n              <li><a href=\"https://events.eclipse.org/\" target=\"\">Explore events</a></li><li><a href=\"/events/open-community-meetups/\" target=\"\">Join a community meetup</a></li><li><a href=\"/events/open-community-webinars/\" target=\"\">Attend a webinar</a></li><li><a href=\"https://www.ocxconf.org/\" target=\"_blank\">OCX <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-developer-resources\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-developer-resources\">Developer resources <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-developer-resources\">\n              <li><a href=\"/projects/\" target=\"\">Explore projects hub</a></li><li><a href=\"/projects/calendar/#office-hours\" target=\"\">Attend office hours</a></li><li><a href=\"/security/\" target=\"\">Report a vulnerability</a></li><li><a href=\"/security/known/\" target=\"\">View known vulnerabilities</a></li><li><a href=\"https://github.com/eclipse-csi/security-handbook/blob/main/docs/vulnerabilities/advisories.md\" target=\"_blank\">Use the security handbook</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-industry-insights\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-industry-insights\">Industry insights <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-industry-insights\">\n              <li><a href=\"/org/value/#success_stories\" target=\"\">View case studies</a></li><li><a href=\"/org/value/#whitepapers\" target=\"\">View whitepapers</a></li><li><a href=\"/org/value/#surveyReports\" target=\"\">View surveys &amp; reports</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-marketplaces\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-marketplaces\">Marketplaces <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-marketplaces\">\n              <li><a href=\"https://adoptium.net/\" target=\"\">Adoptium</a></li><li><a href=\"https://marketplace.eclipse.org/\" target=\"\">Eclipse IDE</a></li><li><a href=\"https://open-vsx.org/\" target=\"\">Open VSX</a></li>\n            </ul>\n          </li>\n          </ul>\n        </li>        <li>\n          <a role=\"button\" href=\"#mobile-menu-about-us\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-about-us\">\n            About us\n            <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n          </a>\n\n          <ul class=\"collapse\" id=\"mobile-menu-about-us\">\n                      <li>\n            <a role=\"button\" href=\"#mobile-menu-the-foundation\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-the-foundation\">The Foundation <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-the-foundation\">\n              <li><a href=\"/about/\" target=\"\">About</a></li><li><a href=\"/org/foundation/staff/\" target=\"\">Meet the team</a></li><li><a href=\"/org/governance/\" target=\"\">Board &amp; governance</a></li><li><a href=\"/org/foundation/eclipseawards/\" target=\"\">Awards</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-our-members\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-our-members\">Our members <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-our-members\">\n              <li><a href=\"/membership/\" target=\"\">Become a member</a></li><li><a href=\"/membership/explore-membership/\" target=\"\">View member directory</a></li><li><a href=\"https://membership.eclipse.org/portal\" target=\"_blank\">Access member portal</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-sponsorship\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-sponsorship\">Sponsorship <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-sponsorship\">\n              <li><a href=\"/sponsor/\" target=\"\">Become a sponsor</a></li><li><a href=\"/org/our-sponsors/\" target=\"\">Sponsor directory</a></li>\n            </ul>\n          </li>          <li>\n            <a role=\"button\" href=\"#mobile-menu-more\" data-toggle=\"collapse\" aria-expanded=\"false\" aria-controls=\"mobile-menu-more\">More <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i></a>\n            <ul class=\"collapse\" id=\"mobile-menu-more\">\n              <li><a href=\"/careers/\" target=\"\">Join our team</a></li><li><a href=\"/org/artwork/\" target=\"\">Explore our brand</a></li><li><a href=\"/org/foundation/contact.php\" target=\"\">Contact us</a></li>\n            </ul>\n          </li>\n          </ul>\n        </li></ul></nav>\n      </div>  <div class=\"mega-menu\">          <div class=\"menu hidden\" id=\"join-us-menu\" aria-expanded=\"false\">\n            <div class=\"container\">\n              <nav>\n                          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-user-group\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Membership</h2>\n            </div>\n            <ul>\n              <li><a href=\"/membership/\" target=\"\">Become a member</a></li><li><a href=\"/membership/#tab-fees\" target=\"\">Review membership fees</a></li><li><a href=\"https://membership.eclipse.org/portal\" target=\"\">Access member portal</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-bookmark\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Sponsorship</h2>\n            </div>\n            <ul>\n              <li><a href=\"/sponsor/\" target=\"\">Become a sponsor</a></li><li><a href=\"/org/our-sponsors/\" target=\"\">Sponsor directory</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-handshake-simple\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Contribute</h2>\n            </div>\n            <ul>\n              <li><a href=\"/contribute/\" target=\"\">How to contribute</a></li><li><a href=\"/projects/training/\" target=\"\">Committer training</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-users\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Collaborate with us</h2>\n            </div>\n            <ul>\n              <li><a href=\"/org/working-groups/explore/\" target=\"\">Join an existing industry collaboration</a></li><li><a href=\"/collaborations/#start-collaboration\" target=\"\">Start a new collaboration</a></li>\n            </ul>\n          </div>\n              </nav>\n            </div>\n          </div>          <div class=\"menu hidden\" id=\"what-we-do-menu\" aria-expanded=\"false\">\n            <div class=\"container\">\n              <nav>\n                          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-puzzle-piece\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Projects</h2>\n            </div>\n            <ul>\n              <li><a href=\"https://projects.eclipse.org/\" target=\"\">Explore projects</a></li><li><a href=\"/specifications/\" target=\"\">View specifications</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-building\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Industry collaborations</h2>\n            </div>\n            <ul>\n              <li><a href=\"/org/working-groups/explore/\" target=\"\">Explore collaborations</a></li><li><a href=\"/org/working-groups/about/\" target=\"\">Join a working group</a></li><li><a href=\"/collaborations/interest-groups/\" target=\"\">Join an interest group</a></li><li><a href=\"/org/value/\" target=\"\">Read success stories</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-laptop-code\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Core services</h2>\n            </div>\n            <ul>\n              <li><a href=\"/org/services/\" target=\"\">Explore core services</a></li><li><a href=\"/security/\" target=\"\">Ensure project security</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-bullseye\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Key initiatives</h2>\n            </div>\n            <ul>\n              <li><a href=\"/topics/edge-and-iot/\" target=\"\">Embedded &amp; IoT</a></li><li><a href=\"/topics/cloud-native/\" target=\"\">Enterprise Java</a></li><li><a href=\"https://dataspaces.eclipse.org/\" target=\"\">Data sovereignty</a></li><li><a href=\"/security/\" target=\"\">Security &amp; privacy</a></li><li><a href=\"/topics/ide/\" target=\"\">Developer tools &amp; IDEs</a></li><li><a href=\"https://aieclipse.org/\" target=\"\">AI &amp; emerging technologies <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li><li><a href=\"/topics/automotive-and-mobility/\" target=\"\">Automotive &amp; mobility</a></li><li><a href=\"https://orcwg.org/\" target=\"\">Policy &amp; compliance <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"grid-column: -3; margin-top: -20rem;\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-flag\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Strategic services</h2>\n            </div>\n            <ul>\n              <li><a href=\"/research/\" target=\"\">Transform research into open source</a></li><li><a href=\"/os4biz/services/\" target=\"\">Engage professional services</a></li><li><a href=\"/os4biz/ospo/\" target=\"\">Build your OSPO</a></li>\n            </ul>\n          </div>\n              </nav>\n            </div>\n          </div>          <div class=\"menu hidden\" id=\"resources-menu\" aria-expanded=\"false\">\n            <div class=\"container\">\n              <nav>\n                          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-bullhorn\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">What's happening</h2>\n            </div>\n            <ul>\n              <li><a href=\"https://blogs.eclipse.org/\" target=\"\">Read our blog</a></li><li><a href=\"https://accounts.eclipse.org/mailing-list\" target=\"\">Join mailing lists</a></li><li><a href=\"https://newsroom.eclipse.org/\" target=\"\">Get news updates</a></li><li><a href=\"/community/eclipse_newsletter\" target=\"\">Subscribe to newsletter</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-calendar-days\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Events</h2>\n            </div>\n            <ul>\n              <li><a href=\"https://events.eclipse.org/\" target=\"\">Explore events</a></li><li><a href=\"/events/open-community-meetups/\" target=\"\">Join a community meetup</a></li><li><a href=\"/events/open-community-webinars/\" target=\"\">Attend a webinar</a></li><li><a href=\"https://www.ocxconf.org/\" target=\"_blank\">OCX <i class=\"fa-solid fa-arrow-up-right-from-square external-icon\" aria-hidden=\"true\"></i></a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-code\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Developer resources</h2>\n            </div>\n            <ul>\n              <li><a href=\"/projects/\" target=\"\">Explore projects hub</a></li><li><a href=\"/projects/calendar/#office-hours\" target=\"\">Attend office hours</a></li><li><a href=\"/security/\" target=\"\">Report a vulnerability</a></li><li><a href=\"/security/known/\" target=\"\">View known vulnerabilities</a></li><li><a href=\"https://github.com/eclipse-csi/security-handbook/blob/main/docs/vulnerabilities/advisories.md\" target=\"_blank\">Use the security handbook</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-gear\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Industry insights</h2>\n            </div>\n            <ul>\n              <li><a href=\"/org/value/#success_stories\" target=\"\">View case studies</a></li><li><a href=\"/org/value/#whitepapers\" target=\"\">View whitepapers</a></li><li><a href=\"/org/value/#surveyReports\" target=\"\">View surveys &amp; reports</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"grid-column: -2; margin-top: -6rem;\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-store\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Marketplaces</h2>\n            </div>\n            <ul>\n              <li><a href=\"https://adoptium.net/\" target=\"\">Adoptium</a></li><li><a href=\"https://marketplace.eclipse.org/\" target=\"\">Eclipse IDE</a></li><li><a href=\"https://open-vsx.org/\" target=\"\">Open VSX</a></li>\n            </ul>\n          </div>\n              </nav>\n            </div>\n          </div>          <div class=\"menu hidden\" id=\"about-us-menu\" aria-expanded=\"false\">\n            <div class=\"container\">\n              <nav>\n                          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-moon rotate-90\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">The Foundation</h2>\n            </div>\n            <ul>\n              <li><a href=\"/about/\" target=\"\">About</a></li><li><a href=\"/org/foundation/staff/\" target=\"\">Meet the team</a></li><li><a href=\"/org/governance/\" target=\"\">Board &amp; governance</a></li><li><a href=\"/org/foundation/eclipseawards/\" target=\"\">Awards</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-user-group\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Our members</h2>\n            </div>\n            <ul>\n              <li><a href=\"/membership/\" target=\"\">Become a member</a></li><li><a href=\"/membership/explore-membership/\" target=\"\">View member directory</a></li><li><a href=\"https://membership.eclipse.org/portal\" target=\"_blank\">Access member portal</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-bookmark\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">Sponsorship</h2>\n            </div>\n            <ul>\n              <li><a href=\"/sponsor/\" target=\"\">Become a sponsor</a></li><li><a href=\"/org/our-sponsors/\" target=\"\">Sponsor directory</a></li>\n            </ul>\n          </div>          <div class=\"submenu\" style=\"\">\n            <div class=\"menu-heading-group\">\n              <span class=\"menu-pre\"><i class=\"fa-solid fa-cube\" aria-hidden=\"true\"></i></span><h2 class=\"menu-heading\">More</h2>\n            </div>\n            <ul>\n              <li><a href=\"/careers/\" target=\"\">Join our team</a></li><li><a href=\"/org/artwork/\" target=\"\">Explore our brand</a></li><li><a href=\"/org/foundation/contact.php\" target=\"\">Contact us</a></li>\n            </ul>\n          </div>\n              </nav>\n            </div>\n          </div></div>\n  </header>    <main  id=\"content\">\n      \n      <div  class=\"novaContent container\" id=\"novaContent\">\n        <div class=\"row\">\n              <section class=\"default-breadcrumbs hidden-print breadcrumbs-default-margin\" id=\"breadcrumb\">\n      <div class=\"container\">\n        <h3 class=\"sr-only\">Breadcrumbs</h3>\n        <div class=\"row\">\n          <div class=\"col-sm-24\"><ol class=\"breadcrumb\"><li><a href=\"/\">Home</a></li><li><a href=\"http://www.eclipse.org/org\">About Us</a></li><li><a href=\"http://www.eclipse.org/org/documents\">Governance Documents</a></li><li class=\"active\">Eclipse Distribution License</li></ol></div>\n        </div>\n      </div>\n    </section> <!-- /#breadcrumb -->\n        </div>\n        <div class=\"row\">\n          <div class=\"col-md-6 main-col-sidebar-nav\"><aside  class=\"main-sidebar-default-margin sidebar margin-top-20\" id=\"main-sidebar\">\n  <ul class=\"ul-left-nav\" id=\"leftnav\" role=\"tablist\" aria-multiselectable=\"true\">\n                        <li class=\"main-sidebar-main-item main-sidebar-item-indented separator\">\n          <a\n            class=\"main-sidebar-heading link-unstyled\"\n                          href=\"\"\n                      >\n            About Us          </a>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a class=\"link-unstyled\" href=\"/org/foundation/\">\n            Foundation          </a>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-2\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-2\"\n          >\n            <span>Members & Supporters</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-2\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/membership/exploreMembership.php\"\n                    target=\"_self\"\n                  >\n                    Our Members                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/corporate_sponsors\"\n                    target=\"_self\"\n                  >\n                    Corporate Sponsors                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/thankyou.php\"\n                    target=\"_self\"\n                  >\n                    In-Kind Supporters                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-6\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-6\"\n          >\n            <span>Services</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-6\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/services/#IP_Management\"\n                    target=\"_self\"\n                  >\n                    IP Management                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/services/#Ecosystem\"\n                    target=\"_self\"\n                  >\n                    Ecosystem Development                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/services/#Development\"\n                    target=\"_self\"\n                  >\n                    Development Process                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/services/#IT\"\n                    target=\"_self\"\n                  >\n                    IT Infrastructure                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/services/marketing\"\n                    target=\"_self\"\n                  >\n                    Marketing Services                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-12\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-12\"\n          >\n            <span>Our Team</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-12\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/staff.php\"\n                    target=\"_self\"\n                  >\n                    Eclipse Foundation Staff                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/contact.php\"\n                    target=\"_self\"\n                  >\n                    Contact Us                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/careers\"\n                    target=\"_self\"\n                  >\n                    Careers                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-16\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-16\"\n          >\n            <span>Governance</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-16\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/directors.php\"\n                    target=\"_self\"\n                  >\n                    Board of Directors                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/elections/\"\n                    target=\"_self\"\n                  >\n                    Board of Directors Elections                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/council.php\"\n                    target=\"_self\"\n                  >\n                    Councils                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/reports/annual_report.php\"\n                    target=\"_self\"\n                  >\n                    Annual Report                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/documents/\"\n                    target=\"_self\"\n                  >\n                    Governance Documents                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/foundation/minutes.php\"\n                    target=\"_self\"\n                  >\n                    Meeting Minutes                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-23\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-23\"\n          >\n            <span>Our Brand</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-23\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/artwork/\"\n                    target=\"_self\"\n                  >\n                    Logos and Artwork                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/legal/documents/eclipse_foundation_branding_guidelines.pdf\"\n                    target=\"_self\"\n                  >\n                    Eclipse Foundation Brand Guidelines                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/legal/logo_guidelines.php\"\n                    target=\"_self\"\n                  >\n                    Trademark Usage Guidelines                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/documents/writing-style-guide/\"\n                    target=\"_self\"\n                  >\n                    Writing Style Guide                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                              <li class=\"main-sidebar-item main-sidebar-item-indented\">\n          <a\n            class=\"link-unstyled\"\n            role=\"button\"\n            href=\"#sidebar-item-28\"\n            data-toggle=\"collapse\"\n            aria-expanded=\"false\"\n            aria-controls=\"sidebar-item-28\"\n          >\n            <span>Announcements</span>\n          </a>\n          <div class=\"collapse\" id=\"sidebar-item-28\">\n            <ul class=\"main-sidebar-item-submenu list-unstyled\">\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/press-release/\"\n                    target=\"_self\"\n                  >\n                    Press Releases                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/press-release/pressrelease_guidelines.php\"\n                    target=\"_self\"\n                  >\n                    PR Guidelines                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/press-release/2006archive.php\"\n                    target=\"_self\"\n                  >\n                    2006 Archive                  </a>\n                </li>\n                              <li class=\"submenu-item\">\n                  <a\n                    class=\"link-unstyled\"\n                    href=\"/org/press-release/main.html\"\n                    target=\"_self\"\n                  >\n                    2001-2005 Archive                  </a>\n                </li>\n                          </ul>\n          </div>\n        </li>\n                </aside>\n  </div>\n          <div class=\"col-md-18 main-col-content\">\n            <div class=\" main-col-content\">\n              \n\t<div id=\"midcolumn\">\n\t\t<h1>Eclipse Distribution License  - v 1.0</h1>\n\n<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>\n\n<p>All rights reserved.</p>\n<p>Redistribution and use in source and binary forms, with or without modification, \n\tare permitted provided that the following conditions are met:</p>\n<ul><li>Redistributions of source code must retain the above copyright notice, \n\tthis list of conditions and the following disclaimer. </li>\n<li>Redistributions in binary form must reproduce the above copyright notice, \n\tthis list of conditions and the following disclaimer in the documentation \n\tand/or other materials provided with the distribution. </li>\n<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its \n\tcontributors may be used to endorse or promote products derived from \n\tthis software without specific prior written permission. </li></ul>\n\n<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" \nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \nIN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \nNOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \nPOSSIBILITY OF SUCH DAMAGE.</p>\n</div>\n\t<div id=\"rightcolumn\">\n\t\t<div class=\"sideitem\">\n\t\t\t<h6>OSI Approved</h6>The Eclipse Distribution License is an OSI Approved Open Source License by means of the \n\t\t\t<a href=\"http://www.opensource.org/licenses/BSD-3-Clause\" target=\"blank\">New BSD License</a>.\n\t\t\t<p align=\"center\"><a href=\"http://www.opensource.org/licenses/BSD-3-Clause\" target=\"blank\"><img align=\"center\" src=\"../../images/osi-certified-60x50.gif\" border=\"0\"/>&nbsp</a></p>\n\t\t\t<h6>Purpose</h6>\n\t\t\t<p>Use of the Eclipse Distribution License by any project at the Eclipse Foundation must \n\t\t\tbe reviewed and unanimously approved by the <a href=\"../foundation/directors.php\">Board of Directors</a>.</p>\n\t\t\t<h6>SPDX License Identifier</h6>\n\t\t\t<p><code>BSD-3-Clause</code></p>\n\t\t\t<h6>Related Links</h6>\n\t\t\t<ul>\n\t\t\t<li>The standard Eclipse license is the <a href=\"/legal/epl-2.0/\">Eclipse Public License</a>.</li>\n\t\t\t<li><a href=\"edl-v10.html\">EDL in plain HTML</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n\n            </div>\n          </div>\n        </div>\n      </div>\n    </main>  <aside class=\"eclipsefdn-featured-footer featured-footer\" id=\"featured-footer\" data-publish-target=\"eclipse_org\"><div class=\"container\"><div class=\"row\"><div class=\"col-sm-24 featured-container\"></div></div></div></aside>\n<footer class=\"footer\">\n  <div class=\"container\">\n    <div class=\"footer-inner\">\n      <section class=\"links-section\">\n              <ul>\n      <li>Eclipse Foundation</li>\n      <ul><li><a href=\"http://www.eclipse.org/org/\" class=\"\">About</a></li><li><a href=\"https://projects.eclipse.org/\" class=\"\">Projects</a></li><li><a href=\"http://www.eclipse.org/collaborations/\" class=\"\">Collaborations</a></li><li><a href=\"http://www.eclipse.org/membership/\" class=\"\">Membership</a></li><li><a href=\"http://www.eclipse.org/sponsor/\" class=\"\">Sponsor</a></li></ul>\n      </ul>              <ul>\n      <li>Legal</li>\n      <ul><li><a href=\"http://www.eclipse.org/legal/privacy.php\" class=\"\">Privacy Policy</a></li><li><a href=\"http://www.eclipse.org/legal/termsofuse.php\" class=\"\">Terms of Use</a></li><li><a href=\"http://www.eclipse.org/legal/compliance/\" class=\"\">Compliance</a></li><li><a href=\"http://www.eclipse.org/org/documents/Community_Code_of_Conduct.php\" class=\"\">Code of Conduct</a></li><li><a href=\"http://www.eclipse.org/legal/\" class=\"\">Legal Resources</a></li><li><a href=\"#\" class=\"toolbar-manage-cookies\">Manage Cookies</a></li></ul>\n      </ul>              <ul>\n      <li>More</li>\n      <ul><li><a href=\"http://www.eclipse.org/security/\" class=\"\">Report a Vulnerability</a></li><li><a href=\"https://www.eclipsestatus.io/\" class=\"\">Service Status</a></li><li><a href=\"http://www.eclipse.org/org/foundation/contact.php\" class=\"\">Contact</a></li><li><a href=\"http://www.eclipse.org//projects/support/\" class=\"\">Support</a></li></ul>\n      </ul>      </section>\n      <section class=\"social-media-section\">\n        <h2 class=\"subheading\">See what we're up to</h2>\n        <ul class=\"social-media-list\">\n\n            <li>\n              <a href=\"https://x.com/EclipseFdn\" aria-label=\"X\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-x-twitter fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://bsky.app/profile/eclipse.org\" aria-label=\"Bluesky\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-bluesky fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://mastodon.social/@EclipseFdn\" aria-label=\"Mastodon\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-mastodon fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://www.facebook.com/eclipse.org\" aria-label=\"Facebook\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-facebook-f fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://www.instagram.com/eclipsefoundation/\" aria-label=\"Instagram\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-instagram fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://www.youtube.com/user/EclipseFdn\" aria-label=\"YouTube\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-youtube fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n            <li>\n              <a href=\"https://www.linkedin.com/company/eclipse-foundation\" aria-label=\"LinkedIn\">\n                <span class=\"fa-stack\">\n                  <i class=\"fa-solid fa-circle fa-stack-2x\" aria-hidden=\"true\"></i>\n                  <i class=\"fa-brands fa-linkedin-in fa-stack-1x fa-inverse\" aria-hidden=\"true\"></i>\n                </span>\n              </a>\n            </li>\n\n        </ul>\n      </section>\n      <section class=\"newsletter-section\">\n        <p class=\"subheading\">Stay up to date</p>\n        <h2>Subscribe to our newsletter</h2>\n        <form class=\"newsletter-form margin-top-40\">\n          <div class=\"text-field-outlined\">\n            <input type=\"email\" placeholder=\"Your email\">\n            <button class=\"newsletter-form-submit\" type=\"submit\" aria-label=\"Submit\">\n              <i class=\"fa-solid fa-arrow-right\" aria-hidden=\"true\"></i>\n            </button>\n          </div>\n        </form>\n      </section>\n      <section class=\"footer-bottom-section\">\n        <img class=\"img-responsive\" src=\"https://webdev.eclipse.org/docs/solstice/legacy/dist/images/logo/eclipse-foundation-white-orange.svg\" alt=\"Eclipse Foundation\">\n        <div class=\"legal-information\">\n          <p id=\"copyright-text\">Copyright &copy; Eclipse Foundation AISBL. All Rights Reserved.</p>\n          <ul class=\"legal-links\">\n            <li><a href=\"https://www.eclipse.org/legal/privacy/\">Privacy policy</a></li>\n            <li><a href=\"https://www.eclipse.org/legal/terms-of-use/\">Terms of use</a></li>\n            <li><a href=\"https://www.eclipse.org/legal/compliance/\">Compliance</a></li>\n            <li><a href=\"https://www.eclipse.org/legal/\">Legal</a></li>\n          </ul>\n        </div>\n      </section>\n    </div>\n  </div>\n</footer>\n<!-- Placed at the end of the document so the pages load faster -->\n<script src=\"https://www.eclipse.org/public/js/main.js?v=0.1.3\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/eclipse public license 1.0 - epl-v10.html",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\" />\n<title>Eclipse Public License - Version 1.0</title>\n<style type=\"text/css\">\n  body {\n    size: 8.5in 11.0in;\n    margin: 0.25in 0.5in 0.25in 0.5in;\n    tab-interval: 0.5in;\n    }\n  p {  \t\n    margin-left: auto;\n    margin-top:  0.5em;\n    margin-bottom: 0.5em;\n    }\n  p.list {\n  \tmargin-left: 0.5in;\n    margin-top:  0.05em;\n    margin-bottom: 0.05em;\n    }\n  </style>\n\n</head>\n\n<body lang=\"EN-US\">\n\n<h2>Eclipse Public License - v 1.0</h2>\n\n<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE\nPUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR\nDISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS\nAGREEMENT.</p>\n\n<p><b>1. DEFINITIONS</b></p>\n\n<p>&quot;Contribution&quot; means:</p>\n\n<p class=\"list\">a) in the case of the initial Contributor, the initial\ncode and documentation distributed under this Agreement, and</p>\n<p class=\"list\">b) in the case of each subsequent Contributor:</p>\n<p class=\"list\">i) changes to the Program, and</p>\n<p class=\"list\">ii) additions to the Program;</p>\n<p class=\"list\">where such changes and/or additions to the Program\noriginate from and are distributed by that particular Contributor. A\nContribution 'originates' from a Contributor if it was added to the\nProgram by such Contributor itself or anyone acting on such\nContributor's behalf. Contributions do not include additions to the\nProgram which: (i) are separate modules of software distributed in\nconjunction with the Program under their own license agreement, and (ii)\nare not derivative works of the Program.</p>\n\n<p>&quot;Contributor&quot; means any person or entity that distributes\nthe Program.</p>\n\n<p>&quot;Licensed Patents&quot; mean patent claims licensable by a\nContributor which are necessarily infringed by the use or sale of its\nContribution alone or when combined with the Program.</p>\n\n<p>&quot;Program&quot; means the Contributions distributed in accordance\nwith this Agreement.</p>\n\n<p>&quot;Recipient&quot; means anyone who receives the Program under\nthis Agreement, including all Contributors.</p>\n\n<p><b>2. GRANT OF RIGHTS</b></p>\n\n<p class=\"list\">a) Subject to the terms of this Agreement, each\nContributor hereby grants Recipient a non-exclusive, worldwide,\nroyalty-free copyright license to reproduce, prepare derivative works\nof, publicly display, publicly perform, distribute and sublicense the\nContribution of such Contributor, if any, and such derivative works, in\nsource code and object code form.</p>\n\n<p class=\"list\">b) Subject to the terms of this Agreement, each\nContributor hereby grants Recipient a non-exclusive, worldwide,\nroyalty-free patent license under Licensed Patents to make, use, sell,\noffer to sell, import and otherwise transfer the Contribution of such\nContributor, if any, in source code and object code form. This patent\nlicense shall apply to the combination of the Contribution and the\nProgram if, at the time the Contribution is added by the Contributor,\nsuch addition of the Contribution causes such combination to be covered\nby the Licensed Patents. The patent license shall not apply to any other\ncombinations which include the Contribution. No hardware per se is\nlicensed hereunder.</p>\n\n<p class=\"list\">c) Recipient understands that although each Contributor\ngrants the licenses to its Contributions set forth herein, no assurances\nare provided by any Contributor that the Program does not infringe the\npatent or other intellectual property rights of any other entity. Each\nContributor disclaims any liability to Recipient for claims brought by\nany other entity based on infringement of intellectual property rights\nor otherwise. As a condition to exercising the rights and licenses\ngranted hereunder, each Recipient hereby assumes sole responsibility to\nsecure any other intellectual property rights needed, if any. For\nexample, if a third party patent license is required to allow Recipient\nto distribute the Program, it is Recipient's responsibility to acquire\nthat license before distributing the Program.</p>\n\n<p class=\"list\">d) Each Contributor represents that to its knowledge it\nhas sufficient copyright rights in its Contribution, if any, to grant\nthe copyright license set forth in this Agreement.</p>\n\n<p><b>3. REQUIREMENTS</b></p>\n\n<p>A Contributor may choose to distribute the Program in object code\nform under its own license agreement, provided that:</p>\n\n<p class=\"list\">a) it complies with the terms and conditions of this\nAgreement; and</p>\n\n<p class=\"list\">b) its license agreement:</p>\n\n<p class=\"list\">i) effectively disclaims on behalf of all Contributors\nall warranties and conditions, express and implied, including warranties\nor conditions of title and non-infringement, and implied warranties or\nconditions of merchantability and fitness for a particular purpose;</p>\n\n<p class=\"list\">ii) effectively excludes on behalf of all Contributors\nall liability for damages, including direct, indirect, special,\nincidental and consequential damages, such as lost profits;</p>\n\n<p class=\"list\">iii) states that any provisions which differ from this\nAgreement are offered by that Contributor alone and not by any other\nparty; and</p>\n\n<p class=\"list\">iv) states that source code for the Program is available\nfrom such Contributor, and informs licensees how to obtain it in a\nreasonable manner on or through a medium customarily used for software\nexchange.</p>\n\n<p>When the Program is made available in source code form:</p>\n\n<p class=\"list\">a) it must be made available under this Agreement; and</p>\n\n<p class=\"list\">b) a copy of this Agreement must be included with each\ncopy of the Program.</p>\n\n<p>Contributors may not remove or alter any copyright notices contained\nwithin the Program.</p>\n\n<p>Each Contributor must identify itself as the originator of its\nContribution, if any, in a manner that reasonably allows subsequent\nRecipients to identify the originator of the Contribution.</p>\n\n<p><b>4. COMMERCIAL DISTRIBUTION</b></p>\n\n<p>Commercial distributors of software may accept certain\nresponsibilities with respect to end users, business partners and the\nlike. While this license is intended to facilitate the commercial use of\nthe Program, the Contributor who includes the Program in a commercial\nproduct offering should do so in a manner which does not create\npotential liability for other Contributors. Therefore, if a Contributor\nincludes the Program in a commercial product offering, such Contributor\n(&quot;Commercial Contributor&quot;) hereby agrees to defend and\nindemnify every other Contributor (&quot;Indemnified Contributor&quot;)\nagainst any losses, damages and costs (collectively &quot;Losses&quot;)\narising from claims, lawsuits and other legal actions brought by a third\nparty against the Indemnified Contributor to the extent caused by the\nacts or omissions of such Commercial Contributor in connection with its\ndistribution of the Program in a commercial product offering. The\nobligations in this section do not apply to any claims or Losses\nrelating to any actual or alleged intellectual property infringement. In\norder to qualify, an Indemnified Contributor must: a) promptly notify\nthe Commercial Contributor in writing of such claim, and b) allow the\nCommercial Contributor to control, and cooperate with the Commercial\nContributor in, the defense and any related settlement negotiations. The\nIndemnified Contributor may participate in any such claim at its own\nexpense.</p>\n\n<p>For example, a Contributor might include the Program in a commercial\nproduct offering, Product X. That Contributor is then a Commercial\nContributor. If that Commercial Contributor then makes performance\nclaims, or offers warranties related to Product X, those performance\nclaims and warranties are such Commercial Contributor's responsibility\nalone. Under this section, the Commercial Contributor would have to\ndefend claims against the other Contributors related to those\nperformance claims and warranties, and if a court requires any other\nContributor to pay any damages as a result, the Commercial Contributor\nmust pay those damages.</p>\n\n<p><b>5. NO WARRANTY</b></p>\n\n<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS\nPROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS\nOF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,\nANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY\nOR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely\nresponsible for determining the appropriateness of using and\ndistributing the Program and assumes all risks associated with its\nexercise of rights under this Agreement , including but not limited to\nthe risks and costs of program errors, compliance with applicable laws,\ndamage to or loss of data, programs or equipment, and unavailability or\ninterruption of operations.</p>\n\n<p><b>6. DISCLAIMER OF LIABILITY</b></p>\n\n<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT\nNOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING\nWITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR\nDISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED\nHEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>\n\n<p><b>7. GENERAL</b></p>\n\n<p>If any provision of this Agreement is invalid or unenforceable under\napplicable law, it shall not affect the validity or enforceability of\nthe remainder of the terms of this Agreement, and without further action\nby the parties hereto, such provision shall be reformed to the minimum\nextent necessary to make such provision valid and enforceable.</p>\n\n<p>If Recipient institutes patent litigation against any entity\n(including a cross-claim or counterclaim in a lawsuit) alleging that the\nProgram itself (excluding combinations of the Program with other\nsoftware or hardware) infringes such Recipient's patent(s), then such\nRecipient's rights granted under Section 2(b) shall terminate as of the\ndate such litigation is filed.</p>\n\n<p>All Recipient's rights under this Agreement shall terminate if it\nfails to comply with any of the material terms or conditions of this\nAgreement and does not cure such failure in a reasonable period of time\nafter becoming aware of such noncompliance. If all Recipient's rights\nunder this Agreement terminate, Recipient agrees to cease use and\ndistribution of the Program as soon as reasonably practicable. However,\nRecipient's obligations under this Agreement and any licenses granted by\nRecipient relating to the Program shall continue and survive.</p>\n\n<p>Everyone is permitted to copy and distribute copies of this\nAgreement, but in order to avoid inconsistency the Agreement is\ncopyrighted and may only be modified in the following manner. The\nAgreement Steward reserves the right to publish new versions (including\nrevisions) of this Agreement from time to time. No one other than the\nAgreement Steward has the right to modify this Agreement. The Eclipse\nFoundation is the initial Agreement Steward. The Eclipse Foundation may\nassign the responsibility to serve as the Agreement Steward to a\nsuitable separate entity. Each new version of the Agreement will be\ngiven a distinguishing version number. The Program (including\nContributions) may always be distributed subject to the version of the\nAgreement under which it was received. In addition, after a new version\nof the Agreement is published, Contributor may elect to distribute the\nProgram (including its Contributions) under the new version. Except as\nexpressly stated in Sections 2(a) and 2(b) above, Recipient receives no\nrights or licenses to the intellectual property of any Contributor under\nthis Agreement, whether expressly, by implication, estoppel or\notherwise. All rights in the Program not expressly granted under this\nAgreement are reserved.</p>\n\n<p>This Agreement is governed by the laws of the State of New York and\nthe intellectual property laws of the United States of America. No party\nto this Agreement will bring a legal action under this Agreement more\nthan one year after the cause of action arose. Each party waives its\nrights to a jury trial in any resulting litigation.</p>\n\n</body>\n\n</html>"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/epl 1.0 - eclipse-1.0.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>Eclipse Public License -v 1.0 &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fepl-1-0\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fepl-1-0&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.1.2' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://opensource.org/wp-content/plugins/jetpack/_inc/genericons/genericons/genericons.css?ver=3.1' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='nelio-content-premium-series-default-css-css' href='https://opensource.org/wp-content/plugins/nelio-content-premium/includes/features/series/default.css?ver=4.0.9' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"f34ba0dbf0\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1764787278\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/472\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/epl-1-0\" />\n<link rel='shortlink' href='https://opensource.org/?p=472' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"Eclipse Public License -v 1.0\" />\n\t<meta property=\"og:description\" content=\"THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&#8220;AGREEMENT&#8221;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&#8217;S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/epl-1-0/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-472 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-472\" class=\"post-472 license type-license status-publish hentry taxonomy-steward-eclipse-foundation taxonomy-license-category-superseded\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Superseded\" href=\"https://opensource.org/licenses/?categories=superseded\" data-id=\"27\" data-term=\"superseded\">Superseded</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">Eclipse Public License -v 1.0</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t<span class=\"license-version\">Version 1.0</span><span class=\"license-release\">Submitted: <a href=\"http://lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2004-March/007932.html\" target=\"_blank\">March 8, 2004</a></span><span class=\"license-submitter\">Submitter: Philip Ma</span>\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tEPL-1.0\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"license-steward-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-steward\">Steward:<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Eclipse Foundation\" href=\"https://opensource.org/license/steward/eclipse-foundation\" data-id=\"30\" data-term=\"eclipse-foundation\">Eclipse Foundation</a></li></ul></div>\n</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-steward-url\">\n\t\t\t\t\t\t\t\t\t<a href=\"https://www.eclipse.org/org/documents/epl-v10.html\" target=\"_blank\">\n\t\t\t\t\t\t\t\t\t\tLink to license steward&#039;s version\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&#8220;AGREEMENT&#8221;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&#8217;S ACCEPTANCE OF THIS AGREEMENT.</p>\n<p>1. DEFINITIONS</p>\n<p>&#8220;Contribution&#8221; means:</p>\n<p>a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and</p>\n<p>b) in the case of each subsequent Contributor:</p>\n<p>i) changes to the Program, and</p>\n<p>ii) additions to the Program;</p>\n<p>where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution &#8216;originates&#8217; from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor&#8217;s behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.</p>\n<p>&#8220;Contributor&#8221; means any person or entity that distributes the Program.</p>\n<p>&#8220;Licensed Patents &#8221; mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.</p>\n<p>&#8220;Program&#8221; means the Contributions distributed in accordance with this Agreement.</p>\n<p>&#8220;Recipient&#8221; means anyone who receives the Program under this Agreement, including all Contributors.</p>\n<p>2. GRANT OF RIGHTS</p>\n<p>a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</p>\n<p>b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.</p>\n<p>c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient&#8217;s responsibility to acquire that license before distributing the Program.</p>\n<p>d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.</p>\n<p>3. REQUIREMENTS</p>\n<p>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</p>\n<p>a) it complies with the terms and conditions of this Agreement; and</p>\n<p>b) its license agreement:</p>\n<p>i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;</p>\n<p>ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;</p>\n<p>iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</p>\n<p>iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</p>\n<p>When the Program is made available in source code form:</p>\n<p>a) it must be made available under this Agreement; and</p>\n<p>b) a copy of this Agreement must be included with each copy of the Program.</p>\n<p>Contributors may not remove or alter any copyright notices contained within the Program.</p>\n<p>Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.</p>\n<p>4. COMMERCIAL DISTRIBUTION</p>\n<p>Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (&#8220;Commercial Contributor&#8221;) hereby agrees to defend and indemnify every other Contributor (&#8220;Indemnified Contributor&#8221;) against any losses, damages and costs (collectively &#8220;Losses&#8221;) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.</p>\n<p>For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor&#8217;s responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</p>\n<p>5. NO WARRANTY</p>\n<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN &#8220;AS IS&#8221; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.</p>\n<p>6. DISCLAIMER OF LIABILITY</p>\n<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>\n<p>7. GENERAL</p>\n<p>If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</p>\n<p>If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient&#8217;s patent(s), then such Recipient&#8217;s rights granted under Section 2(b) shall terminate as of the date such litigation is filed.</p>\n<p>All Recipient&#8217;s rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient&#8217;s rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient&#8217;s obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.</p>\n<p>Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.</p>\n<p>This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.</p>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-472 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1763999744719\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1763999746738\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1763999744720\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%22f34ba0dbf0%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"472\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3-beta.3\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"472\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"123\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=123\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e167d3d50da97',t:'MTc2NTA0ODI3Mw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated in 0.876 seconds\n\t151330 bytes batcached for 300 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/gnu lesser general public license version 3 - lgpl-3.0.en.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<link rel=\"author\" href=\"mailto:webmasters@gnu.org\" />\n<link rel=\"icon\" type=\"image/png\" href=\"/graphics/gnu-head-mini.png\" />\n<meta name=\"ICBM\" content=\"42.355469,-71.058627\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/layout.min.css\" media=\"screen\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/print.min.css\" media=\"print\" />\n\n\n\n<!-- Parent-Version: 1.98 -->\n<!-- This page is derived from /server/standards/boilerplate.html -->\n<title>GNU Lesser General Public License v3.0\n- GNU Project - Free Software Foundation</title>\n<style type=\"text/css\" media=\"screen\"><!--\n#content .button { margin: 2em 1.5em; }\n@media (max-width:30em) { #content img.imgright { display: none; }}\n--></style>\n<link rel=\"alternate\" type=\"application/rdf+xml\"\n      href=\"/licenses/lgpl-3.0.rdf\" /> \n<!-- begin translist file -->\n\n<link rel=\"alternate\" type=\"text/html\" href=\"/licenses/lgpl-3.0.html\" hreflang=\"x-default\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"en\" hreflang=\"en\" href=\"/licenses/lgpl-3.0.en.html\" title=\"English\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"ca\" hreflang=\"ca\" href=\"/licenses/lgpl-3.0.ca.html\" title=\"català\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"de\" hreflang=\"de\" href=\"/licenses/lgpl-3.0.de.html\" title=\"Deutsch\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"fr\" hreflang=\"fr\" href=\"/licenses/lgpl-3.0.fr.html\" title=\"français\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"ja\" hreflang=\"ja\" href=\"/licenses/lgpl-3.0.ja.html\" title=\"日本語\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"nl\" hreflang=\"nl\" href=\"/licenses/lgpl-3.0.nl.html\" title=\"Nederlands\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"pt-br\" hreflang=\"pt-br\" href=\"/licenses/lgpl-3.0.pt-br.html\" title=\"português\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"ru\" hreflang=\"ru\" href=\"/licenses/lgpl-3.0.ru.html\" title=\"русский\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"tr\" hreflang=\"tr\" href=\"/licenses/lgpl-3.0.tr.html\" title=\"Türkçe\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"uk\" hreflang=\"uk\" href=\"/licenses/lgpl-3.0.uk.html\" title=\"українська\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"zh-cn\" hreflang=\"zh-cn\" href=\"/licenses/lgpl-3.0.zh-cn.html\" title=\"简体中文\" />\n<link rel=\"alternate\" type=\"text/html\" lang=\"zh-tw\" hreflang=\"zh-tw\" href=\"/licenses/lgpl-3.0.zh-tw.html\" title=\"繁體中文\" />\n<!-- end translist file -->\n\n<!-- start of server/banner.html -->\n<!-- start of head-include-2.html -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/server/banners/fundraiser.css\" media=\"screen\" />\n<style type=\"text/css\" media=\"screen\"><!--\n.progress-bar\n{\n  width: 30%;\n}\n--></style>\n\n<style type=\"text/css\" media=\"screen\">\n<!-- TRANSLATORS: Change direction to rtl if you translate\n     the fundraiser and your script is right-to-left.  -->\n#fundraiser { direction: ltr; }\n</style>\n<!-- end of head-include-2.html -->\n\n</head>\n<body>\n<div class=\"inner\">\n<!-- start of server/body-include-1.html -->\n<div id=\"top\">\n<p><a class=\"skip\" href=\"#content\"><b>Skip to main text</b></a></p>\n</div>\n<div id='fundraiser'>\n  <div class=\"message\">\n    <p class=\"headline\"><b>We support your freedom!</b></p>\n    <p><a href=\"https://my.fsf.org/donate?mtm_campaign=winter25&amp;mtm_source=banner\">\n      The free software movement has come a long way in forty years. We want\n      to take a moment to thank the people and projects who have helped bring\n      us to this point, and ask for your support in the decades to come. Help\n      us reach our fundraising goal of $400,000 by January&nbsp;1, 2026 to keep\n      us strong and steadfast in our continuing work.\n    <span class=\"gnun-split\"></span></a></p>\n    <p class=\"button\"><a href=\"https://my.fsf.org/donate?mtm_campaign=winter25&amp;mtm_source=banner\">Donate<span class=\"gnun-split\"></span></a> <a href=\"https://www.fsf.org/appeal?mtm_campaign=winter25&amp;mtm_source=banner\">Read more<span class=\"gnun-split\"></span></a>\n    </p>\n    <div style=\"clear: both\"></div>\n  </div><!-- .message -->\n  <div class=\"progress\">\n    <div class=\"progress-bar\"><span class=\"percentage\">$121,370</span></div>\n    \n    <span class=\"goal\">$400,000<span class=\"gnun-split\"></span>\n    </span>\n    \n  </div><!-- .progress -->\n</div><!-- #fundraiser -->\n\n<div style=\"clear: both\"></div>\n\n<div id=\"header\" role=\"banner\">\n<p id=\"gnu-banner\">\n <a href=\"/\">\n <img src=\"/graphics/heckert_gnu.transp.small.png\" height=\"48\" width=\"49\"\n      alt=\"&nbsp;[A GNU head]&nbsp;\" /><strong>GNU</strong> <span\n class=\"hide\">Operating System</span></a><br />\n <small id=\"fsf-support\">Supported by the\n <a href=\"#mission-statement\">Free Software Foundation</a></small>\n</p>\n\n<div id=\"switches\">\n <div  id=\"search-button\" class=\"switch\">\n  <a href=\"//www.gnu.org/cgi-bin/estseek.cgi\">\n  <img id=\"search-icon\" height=\"30\" width=\"30\"\n       src=\"/graphics/icons/search.png\"\n       alt=\"&nbsp;[Search www.gnu.org]&nbsp;\" /></a>\n </div>\n\n <div id=\"language-button\" class=\"switch\">\n  <a href=\"#language-container\">\n  <img id=\"language-icon\" height=\"30\" width=\"37\"\n       src=\"/graphics/icons/translations.png\"\n       alt=\"&nbsp;[Other languages]&nbsp;\" /></a>\n </div>\n\n</div><!-- #switches -->\n</div><!-- #header -->\n<!-- end of server/body-include-1.html -->\n\n<!-- start of server/body-include-2 -->\n\n\n<div style=\"clear: both\"></div>\n\n<div id=\"navigation\" role=\"navigation\">\n <a id=\"more-links\" href=\"#navigation\" title=\"More...\">\n   <span>Site navigation</span></a>\n <a id=\"less-links\" href=\"#content\"><b>Skip</b></a>\n <ul>\n\n   <li id=\"tabAboutGNU\"><a href=\"/gnu/gnu.html\">ABOUT&nbsp;GNU</a></li>\n\n\n  <li id=\"tabPhilosophy\"><a href=\"/philosophy/philosophy.html\">PHILOSOPHY</a></li>\n\n\n  <li id=\"tabLicenses\" class=\"active\">\n     <span class='no-display'>=</span> \n    <a href=\"/licenses/licenses.html\">LICENSES</a>\n    <span class=\"gnun-split\"></span>\n     <span class='no-display'>=</span> \n  </li>\n\n\n  <li id=\"tabEducation\"><a href=\"/education/education.html\">EDUCATION</a></li>\n\n\n  <li id=\"tabSoftware\"><a href=\"/software/software.html\">SOFTWARE</a></li>\n\n\n  <li id=\"tabDistros\"><a href=\"/distros/distros.html\">DISTROS</a></li>\n\n\n  <li id=\"tabDoc\"><a href=\"/doc/doc.html\">DOCS</a></li>\n\n\n  <li id=\"tabMalware\"><a href=\"/proprietary/proprietary.html\">MALWARE</a></li>\n\n\n  <li id=\"tabHelp\"><a href=\"/help/help.html\">HELP&nbsp;GNU</a></li>\n\n\n  <li id=\"tabAV\"><a href=\"/audio-video/audio-video.html\">AUDIO&nbsp;&amp;&nbsp;VIDEO</a></li>\n\n\n  <li id=\"tabArt\"><a href=\"/graphics/graphics.html\">GNU&nbsp;ART</a></li>\n\n\n  <li id=\"tabFun\"><a href=\"/fun/humor.html\">FUN</a></li>\n\n\n  <li id=\"tabPeople\"><a href=\"/people/people.html\">GNU'S&nbsp;WHO?</a></li>\n\n  <li><a href=\"//directory.fsf.org\">SOFTWARE&nbsp;DIRECTORY</a></li>\n  <li><a href=\"https://h-node.org/\">HARDWARE</a></li>\n  <li><a href=\"/server/sitemap.html\">SITEMAP</a></li>\n </ul>\n <div style=\"clear: both\"></div>\n</div><!-- /\"navigation -->\n<!-- end of server/body-include-2 -->\n\n<div id=\"content\" role=\"main\">\n<!-- end of server/banner.html -->\n\n<div class=\"article reduced-width\">\n<h2>GNU Lesser General Public License</h2>\n\n<img class=\"imgright\" src=\"/graphics/lgplv3-147x51.png\" alt=\" [LGPLv3 Logo] \" />\n<p class=\"button\"><b><a href=\"#license-text\">Skip to license text</a></b></p>\n\n<ul>\n  <li><a href=\"/licenses/why-not-lgpl.html\">Why you shouldn't use the\n       Lesser GPL for your next library</a></li>\n  <li><a href=\"/licenses/gpl-faq.html\">Frequently Asked Questions about\n       the GNU licenses</a></li>\n  <li><a href=\"/licenses/gpl-howto.html\">How to use GNU licenses for your\n       own software</a></li>\n  <li><a href=\"/licenses/translations.html\">Translations\n       of the LGPL</a></li>\n  <li>The GNU LGPL in other formats: \n       <a href=\"/licenses/lgpl-3.0.txt\">plain text</a>,\n       <a href=\"/licenses/lgpl-3.0.dbk\">Docbook</a>,\n       <a href=\"/licenses/lgpl-3.0-standalone.html\">standalone HTML</a>,\n       <a href=\"/licenses/lgpl-3.0.tex\">LaTeX</a>,\n       <a href=\"/licenses/lgpl-3.0.texi\">Texinfo</a>,\n       <a href=\"/licenses/lgpl-3.0.md\">Markdown</a>,\n       <a href=\"/licenses/lgpl-3.0.odt\">ODF</a>,\n       <a href=\"/licenses/lgpl-3.0.rtf\">RTF</a></li>\n  <li>The combined LGPL and GPL:\n       <a href=\"/licenses/lgpl+gpl-3.0.txt\">plain text</a>,\n       <a href=\"/licenses/lgpl+gpl-3.0-standalone.html\">standalone HTML</a>,\n       <a href=\"/licenses/lgpl+gpl-3.0.md\">Markdown</a></li>\n  <li><a href=\"/graphics/license-logos.html\">LGPLv3 logos</a> to use\n       with your project</li>\n  <li><a href=\"/licenses/old-licenses/old-licenses.html#LGPL\">Old\n       versions of the GNU LGPL</a></li>\n  <li><a href=\"/licenses/gpl-violation.html\">What to do if you see a\n       possible LGPL violation</a></li>\n</ul>\n\n<p>This license is a set of additional permissions added to <a\nhref=\"/licenses/gpl-3.0.html\">version 3 of the GNU General Public\nLicense</a>.  For more information about how to release your own software\nunder this license, please see our <a href=\"/licenses/gpl-howto.html\">page\nof instructions</a>.</p>\n\n<hr class=\"thin\" />\n\n<!-- The license text is in English and appears broken in RTL as\n     Arabic, Farsi, etc.  Explicitly set the direction to override the\n     one defined in the translation. -->\n<div id=\"license-text\" dir=\"ltr\">\n<h3 style=\"text-align: center;\">GNU LESSER GENERAL PUBLIC LICENSE</h3>\n<p style=\"text-align: center;\">Version 3, 29 June 2007</p>\n\n<p>Copyright &copy; 2007 Free Software Foundation, Inc.\n &lt;<a href=\"https://fsf.org/\">https://fsf.org/</a>&gt;</p><p>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.</p>\n\n<p>This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.</p>\n\n<h4 id=\"section0\">0. Additional Definitions.</h4>\n\n<p>As used herein, &ldquo;this License&rdquo; refers to version 3 of the GNU Lesser\nGeneral Public License, and the &ldquo;GNU GPL&rdquo; refers to version 3 of the GNU\nGeneral Public License.</p>\n\n<p>&ldquo;The Library&rdquo; refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.</p>\n\n<p>An &ldquo;Application&rdquo; is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.</p>\n\n<p>A &ldquo;Combined Work&rdquo; is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the &ldquo;Linked\nVersion&rdquo;.</p>\n\n<p>The &ldquo;Minimal Corresponding Source&rdquo; for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.</p>\n\n<p>The &ldquo;Corresponding Application Code&rdquo; for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.</p>\n\n<h4 id=\"section1\">1. Exception to Section 3 of the GNU GPL.</h4>\n\n<p>You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.</p>\n\n<h4 id=\"section2\">2. Conveying Modified Versions.</h4>\n\n<p>If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:</p>\n\n<ul>\n<li>a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or</li>\n\n<li>b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.</li>\n</ul>\n\n<h4 id=\"section3\">3. Object Code Incorporating Material from Library Header Files.</h4>\n\n<p>The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:</p>\n\n<ul>\n<li>a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.</li>\n\n<li>b) Accompany the object code with a copy of the GNU GPL and this license\n   document.</li>\n</ul>\n\n<h4 id=\"section4\">4. Combined Works.</h4>\n\n<p>You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:</p>\n\n<ul>\n<li>a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.</li>\n\n<li>b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.</li>\n\n<li>c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.</li>\n\n<li>d) Do one of the following:\n\n<ul>\n<li>0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.</li>\n\n<li>1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.</li>\n</ul></li>\n\n<li>e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)</li>\n</ul>\n\n<h4 id=\"section5\">5. Combined Libraries.</h4>\n\n<p>You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:</p>\n\n<ul>\n<li>a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.</li>\n\n<li>b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.</li>\n</ul>\n\n<h4 id=\"section6\">6. Revised Versions of the GNU Lesser General Public License.</h4>\n\n<p>The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.</p>\n\n<p>Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License &ldquo;or any later version&rdquo;\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.</p>\n\n<p>If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary.</p>\n\n</div>\n</div>\n\n</div><!-- for id=\"content\", starts in the include above -->\n\n              <!-- begin server/footer-text.html -->\n\n\n\n\n\n<div style=\"clear:both\"></div>\n\n\n<div id=\"language-container\">\n <div class=\"backtotop\">\n  <hr class=\"no-display\" />\n  <a href=\"#top\"><b>&#9650;</b></a>\n </div>\n <div id=\"languages\" class=\"rounded-corners\">\n  <div class=\"button\">\n   <a href=\"#top\" class=\"close\"><span>BACK TO TOP</span></a>\n  </div>\n  <div id=\"set-language\" class=\"button\">\n   <span class=\"gnun-split\"></span>\n   <a href=\"/server/select-language.html?callback=/licenses/lgpl-3.0.en.html\" rel=\"nofollow\">\n   Set language\n   <span class=\"gnun-split\"></span>\n   </a>\n  </div>\n  <p>Available for this page:</p>\n  <div id=\"translations\">\n<p>\n<span dir=\"ltr\" class=\"original\">[en]&nbsp;<a lang=\"en\" hreflang=\"en\" href=\"/licenses/lgpl-3.0.en.html\">English</a> &nbsp;</span>\n<span dir=\"ltr\">[ca]&nbsp;<a lang=\"ca\" hreflang=\"ca\" href=\"/licenses/lgpl-3.0.ca.html\">català</a> &nbsp;</span>\n<span dir=\"ltr\">[de]&nbsp;<a lang=\"de\" hreflang=\"de\" href=\"/licenses/lgpl-3.0.de.html\">Deutsch</a> &nbsp;</span>\n<span dir=\"ltr\">[fr]&nbsp;<a lang=\"fr\" hreflang=\"fr\" href=\"/licenses/lgpl-3.0.fr.html\">français</a> &nbsp;</span>\n<span dir=\"ltr\">[ja]&nbsp;<a lang=\"ja\" hreflang=\"ja\" href=\"/licenses/lgpl-3.0.ja.html\">日本語</a> &nbsp;</span>\n<span dir=\"ltr\">[nl]&nbsp;<a lang=\"nl\" hreflang=\"nl\" href=\"/licenses/lgpl-3.0.nl.html\">Nederlands</a> &nbsp;</span>\n<span dir=\"ltr\">[pt-br]&nbsp;<a lang=\"pt-br\" hreflang=\"pt-br\" href=\"/licenses/lgpl-3.0.pt-br.html\">português</a> &nbsp;</span>\n<span dir=\"ltr\">[ru]&nbsp;<a lang=\"ru\" hreflang=\"ru\" href=\"/licenses/lgpl-3.0.ru.html\">русский</a> &nbsp;</span>\n<span dir=\"ltr\">[tr]&nbsp;<a lang=\"tr\" hreflang=\"tr\" href=\"/licenses/lgpl-3.0.tr.html\">Türkçe</a> &nbsp;</span>\n<span dir=\"ltr\">[uk]&nbsp;<a lang=\"uk\" hreflang=\"uk\" href=\"/licenses/lgpl-3.0.uk.html\">українська</a> &nbsp;</span>\n<span dir=\"ltr\">[zh-cn]&nbsp;<a lang=\"zh-cn\" hreflang=\"zh-cn\" href=\"/licenses/lgpl-3.0.zh-cn.html\">简体中文</a> &nbsp;</span>\n<span dir=\"ltr\">[zh-tw]&nbsp;<a lang=\"zh-tw\" hreflang=\"zh-tw\" href=\"/licenses/lgpl-3.0.zh-tw.html\">繁體中文</a> &nbsp;</span>\n</p>\n</div>\n </div>\n</div>\n\n<div id=\"mission-statement\" role=\"complementary\">\n <div class=\"backtotop\">\n  <hr class=\"no-display\" />\n  <a href=\"#header\"><span>BACK TO TOP </span>&#9650;</a>\n </div>\n<div style=\"clear: both\"></div>\n<blockquote>\n<p style=\"direction:ltr; text-align:left\"><a href=\"//www.fsf.org\"><img id=\"fsfbanner\"\nsrc=\"/graphics/fsf-logo-notext-small.png\" alt=\"&nbsp;[FSF logo]&nbsp;\"\nwidth=\"75\" height=\"25\" /></a><strong>\n&ldquo;The Free Software Foundation (FSF) is a nonprofit with a worldwide\nmission to promote computer user freedom. We defend the rights of all\nsoftware users.&rdquo;</strong></p>\n</blockquote>\n\n<div id=\"support-the-fsf\" class=\"button\">\n <a class=\"join\" href=\"//www.fsf.org/associate/support_freedom?referrer=4052\">JOIN</a>\n <a class=\"donate\" href=\"//donate.fsf.org/\">DONATE</a>\n <a class=\"shop\" href=\"//shop.fsf.org/\">SHOP</a>\n</div>\n</div>\n<!-- end server/footer-text.html -->\n\n\n<div id=\"footer\" role=\"contentinfo\">\n<div class=\"unprintable\">\n\n<p>Please send general FSF &amp; GNU inquiries to\n<a href=\"mailto:gnu@gnu.org\">&lt;gnu@gnu.org&gt;</a>.\nThere are also <a href=\"/contact/\">other ways to contact</a>\nthe FSF.  Broken links and other corrections or suggestions can be sent\nto <a href=\"mailto:webmasters@gnu.org\">&lt;webmasters@gnu.org&gt;</a>.</p>\n\n<p><!-- TRANSLATORS: Ignore the original text in this paragraph,\n        replace it with the translation of these two:\n\n        We work hard and do our best to provide accurate, good quality\n        translations.  However, we are not exempt from imperfection.\n        Please send your comments and general suggestions in this regard\n        to <a href=\"mailto:web-translators@gnu.org\">\n        &lt;web-translators@gnu.org&gt;</a>.</p>\n\n        <p>For information on coordinating and contributing translations of\n        our web pages, see <a\n        href=\"/server/standards/README.translations.html\">Translations\n        README</a>. -->\nPlease see the <a\nhref=\"/server/standards/README.translations.html\">Translations\nREADME</a> for information on coordinating and contributing translations\nof this article.</p>\n</div>\n\n<p>Copyright notice above.</p>\n\n<p>\nEveryone is permitted to copy and distribute verbatim copies of this license\ndocument, but changing it is not allowed.\n</p>\n\n<!-- start of server/bottom-notes.html -->\n<div id=\"bottom-notes\" class=\"unprintable\">\n<p><a href=\"//www.fsf.org/about/dmca-notice\">Copyright Infringement Notification</a></p>\n<div id=\"generic\">\n\n\n</div>\n</div>\n<!-- end of server/bottom-notes.html -->\n\n\n<p class=\"unprintable\">Updated:\n<!-- timestamp start -->\n$Date: 2025/11/11 12:29:33 $\n<!-- timestamp end -->\n</p>\n</div>\n</div><!-- for class=\"inner\", starts in the banner include -->\n</body>\n</html>\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/ical4j - license - license.txt",
    "content": "==================\n iCal4j - License\n==================\n\nCopyright (c) 2012, Ben Fortuna\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n\to Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n\to Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n\to Neither the name of Ben Fortuna nor the names of any other contributors\nmay be used to endorse or promote products derived from this software\nwithout specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/mit license - mit-license.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>The MIT License &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; The MIT License Comments Feed\" href=\"https://opensource.org/license/mit/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fmit\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fmit&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.2.0' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://c0.wp.com/p/jetpack/15.3.1/_inc/genericons/genericons/genericons.css' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"7d68a4059a\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1764960125\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/395\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/mit\" />\n<link rel='shortlink' href='https://opensource.org/?p=395' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"The MIT License\" />\n\t<meta property=\"og:description\" content=\"Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt; Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8220;Software&#8221;), to deal in the…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/mit/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-395 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-395\" class=\"post-395 license type-license status-publish hentry taxonomy-license-category-popular-strong-community\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Popular / Strong Community\" href=\"https://opensource.org/licenses/?categories=popular-strong-community\" data-id=\"19\" data-term=\"popular-strong-community\">Popular / Strong Community</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">The MIT License</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t<span class=\"license-version\">Version N/A</span>\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tMIT\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t<div id=\"separator\">\n<p><span style=\"font-family: var(--wp--custom--typography--body--font-family), sans-serif; font-size: revert; color: initial;\">Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt;</span></p>\n</div>\n<div id=\"LicenseText\">\n<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8220;Software&#8221;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>\n<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>\n<p>THE SOFTWARE IS PROVIDED &#8220;AS IS&#8221;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>\n</div>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-395 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1764773745495\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1764773747362\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1764773745605\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%227d68a4059a%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"395\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3.1\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"395\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"124\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=124\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/blog/dpgas-annual-members-meeting-advancing-open-source-dpgs-for-the-public-good\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e16f7cf2fda97',t:'MTc2NTA0ODI5Mw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated 281 seconds ago\n\tgenerated in 0.644 seconds\n\tserved from batcache in 0.003 seconds\n\texpires in 19 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/mpl 2.0 - 2.0.html",
    "content": "\n\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"generator\" content=\"pandoc\">\n  <title>Mozilla Public License, version 2.0</title>\n  <!--[if lt IE 9]>\n    <script src=\"https://www.mozilla.org/media/js/ie/libs/html5shiv.a94a0b700fa2.js\"></script>\n  <![endif]-->\n\n  \n    <link href=\"https://www.mozilla.org/media/css/mpl-2-0.d89bc3bc9d45.css\" rel=\"stylesheet\" type=\"text/css\">\n  \n</head>\n<body>\n<h1 id=\"mozilla-public-license-version-2.0\">Mozilla Public License<br>Version 2.0</h1>\n<h2 id=\"definitions\">1. Definitions</h2>\n<dl>\n<dt>1.1. “Contributor”</dt>\n<dd><p>means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.</p>\n</dd>\n<dt>1.2. “Contributor Version”</dt>\n<dd><p>means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution.</p>\n</dd>\n<dt>1.3. “Contribution”</dt>\n<dd><p>means Covered Software of a particular Contributor.</p>\n</dd>\n<dt>1.4. “Covered Software”</dt>\n<dd><p>means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.</p>\n</dd>\n<dt>1.5. “Incompatible With Secondary Licenses”</dt>\n<dd><p>means</p>\n<ol type=\"a\">\n<li><p>that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or</p></li>\n<li><p>that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.</p></li>\n</ol>\n</dd>\n<dt>1.6. “Executable Form”</dt>\n<dd><p>means any form of the work other than Source Code Form.</p>\n</dd>\n<dt>1.7. “Larger Work”</dt>\n<dd><p>means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.</p>\n</dd>\n<dt>1.8. “License”</dt>\n<dd><p>means this document.</p>\n</dd>\n<dt>1.9. “Licensable”</dt>\n<dd><p>means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.</p>\n</dd>\n<dt>1.10. “Modifications”</dt>\n<dd><p>means any of the following:</p>\n<ol type=\"a\">\n<li><p>any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or</p></li>\n<li><p>any new file in Source Code Form that contains any Covered Software.</p></li>\n</ol>\n</dd>\n<dt>1.11. “Patent Claims” of a Contributor</dt>\n<dd><p>means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.</p>\n</dd>\n<dt>1.12. “Secondary License”</dt>\n<dd><p>means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.</p>\n</dd>\n<dt>1.13. “Source Code Form”</dt>\n<dd><p>means the form of the work preferred for making modifications.</p>\n</dd>\n<dt>1.14. “You” (or “Your”)</dt>\n<dd><p>means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.</p>\n</dd>\n</dl>\n<h2 id=\"license-grants-and-conditions\">2. License Grants and Conditions</h2>\n<h3 id=\"grants\">2.1. Grants</h3>\n<p>Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:</p>\n<ol type=\"a\">\n<li><p>under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and</p></li>\n<li><p>under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.</p></li>\n</ol>\n<h3 id=\"effective-date\">2.2. Effective Date</h3>\n<p>The licenses granted in Section&nbsp;2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.</p>\n<h3 id=\"limitations-on-grant-scope\">2.3. Limitations on Grant Scope</h3>\n<p>The licenses granted in this Section&nbsp;2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section&nbsp;2.1(b) above, no patent license is granted by a Contributor:</p>\n<ol type=\"a\">\n<li><p>for any code that a Contributor has removed from Covered Software; or</p></li>\n<li><p>for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or</p></li>\n<li><p>under Patent Claims infringed by Covered Software in the absence of its Contributions.</p></li>\n</ol>\n<p>This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section&nbsp;3.4).</p>\n<h3 id=\"subsequent-licenses\">2.4. Subsequent Licenses</h3>\n<p>No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section&nbsp;10.2) or under the terms of a Secondary License (if permitted under the terms of Section&nbsp;3.3).</p>\n<h3 id=\"representation\">2.5. Representation</h3>\n<p>Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.</p>\n<h3 id=\"fair-use\">2.6. Fair Use</h3>\n<p>This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.</p>\n<h3 id=\"conditions\">2.7. Conditions</h3>\n<p>Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section&nbsp;2.1.</p>\n<h2 id=\"responsibilities\">3. Responsibilities</h2>\n<h3 id=\"distribution-of-source-form\">3.1. Distribution of Source Form</h3>\n<p>All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.</p>\n<h3 id=\"distribution-of-executable-form\">3.2. Distribution of Executable Form</h3>\n<p>If You distribute Covered Software in Executable Form then:</p>\n<ol type=\"a\">\n<li><p>such Covered Software must also be made available in Source Code Form, as described in Section&nbsp;3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and</p></li>\n<li><p>You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License.</p></li>\n</ol>\n<h3 id=\"distribution-of-a-larger-work\">3.3. Distribution of a Larger Work</h3>\n<p>You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).</p>\n<h3 id=\"notices\">3.4. Notices</h3>\n<p>You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.</p>\n<h3 id=\"application-of-additional-terms\">3.5. Application of Additional Terms</h3>\n<p>You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.</p>\n<h2 id=\"inability-to-comply-due-to-statute-or-regulation\">4. Inability to Comply Due to Statute or Regulation</h2>\n<p>If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.</p>\n<h2 id=\"termination\">5. Termination</h2>\n<p>5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.</p>\n<p>5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section&nbsp;2.1 of this License shall terminate.</p>\n<p>5.3. In the event of termination under Sections&nbsp;5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.</p>\n<h2 id=\"disclaimer-of-warranty\">6. Disclaimer of Warranty</h2>\n<p><em>Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.</em></p>\n<h2 id=\"limitation-of-liability\">7. Limitation of Liability</h2>\n<p><em>Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.</em></p>\n<h2 id=\"litigation\">8. Litigation</h2>\n<p>Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims.</p>\n<h2 id=\"miscellaneous\">9. Miscellaneous</h2>\n<p>This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.</p>\n<h2 id=\"versions-of-the-license\">10. Versions of the License</h2>\n<h3 id=\"new-versions\">10.1. New Versions</h3>\n<p>Mozilla Foundation is the license steward. Except as provided in Section&nbsp;10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.</p>\n<h3 id=\"effect-of-new-versions\">10.2. Effect of New Versions</h3>\n<p>You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.</p>\n<h3 id=\"modified-versions\">10.3. Modified Versions</h3>\n<p>If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).</p>\n<h3 id=\"distributing-source-code-form-that-is-incompatible-with-secondary-licenses\">10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses</h3>\n<p>If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.</p>\n<h2 id=\"exhibit-a---source-code-form-license-notice\">Exhibit A - Source Code Form License Notice</h2>\n<blockquote>\n<p>This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.</p>\n</blockquote>\n<p>If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.</p>\n<p>You may add additional accurate notices of copyright ownership.</p>\n<h2 id=\"exhibit-b---incompatible-with-secondary-licenses-notice\">Exhibit B - “Incompatible With Secondary Licenses” Notice</h2>\n<blockquote>\n<p>This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.</p>\n</blockquote>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/new bsd license - bsd-license.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>The 2-Clause BSD License &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; The 2-Clause BSD License Comments Feed\" href=\"https://opensource.org/license/bsd-2-clause/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fbsd-2-clause\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fbsd-2-clause&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.2.0' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://c0.wp.com/p/jetpack/15.3.1/_inc/genericons/genericons/genericons.css' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"7d68a4059a\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1764960125\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/373\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/bsd-2-clause\" />\n<link rel='shortlink' href='https://opensource.org/?p=373' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"The 2-Clause BSD License\" />\n\t<meta property=\"og:description\" content=\"Note: This license has also been called the &#8220;Simplified BSD License&#8221; and the &#8220;FreeBSD License&#8221;. See also the 3-clause BSD License. Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt; Redistribution and use in source…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/BSD-2-Clause/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta property=\"og:image\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2006/07/OSI_Approved_License.png?fit=873%2C1200&amp;quality=80&amp;ssl=1\" />\n\t<meta property=\"og:image:width\" content=\"873\" />\n\t<meta property=\"og:image:height\" content=\"1200\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<meta name=\"twitter:image\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2006/07/OSI_Approved_License.png?fit=873%2C1200&amp;quality=80&amp;ssl=1\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-373 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-373\" class=\"post-373 license type-license status-publish has-post-thumbnail hentry taxonomy-license-category-popular-strong-community\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Popular / Strong Community\" href=\"https://opensource.org/licenses/?categories=popular-strong-community\" data-id=\"19\" data-term=\"popular-strong-community\">Popular / Strong Community</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">The 2-Clause BSD License</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tBSD-2-Clause\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t<p><em style=\"font-family: var(--wp--custom--typography--body--font-family), sans-serif; font-size: revert; color: initial;\">Note: This license has also been called the &#8220;Simplified BSD License&#8221; and the &#8220;FreeBSD License&#8221;. See also the <a href=\"https://opensource.org/licenses/bsd-3-clause/\">3-clause BSD License</a>.</em></p>\n<p>Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt;</p>\n<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>\n<p>1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</p>\n<p>2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.</p>\n<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &#8220;AS IS&#8221; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-373 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1764773745495\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1764773747362\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1764773745605\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%227d68a4059a%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"373\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3.1\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"373\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"124\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=124\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/ai/panels/focusing-on-legal-aspects-of-ai\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e16f12972da97',t:'MTc2NTA0ODI5Mg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated 8 seconds ago\n\tgenerated in 0.774 seconds\n\tserved from batcache in 0.003 seconds\n\texpires in 292 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/the apache license, version 2.0 - license-2.0.txt",
    "content": "\n                                 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": "BORGCalendar/install/src/main/resources/licenses/the apache software license, version 2.0 - license-2.0.txt",
    "content": "\n                                 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": "BORGCalendar/install/src/main/resources/licenses/the mit license - license.txt",
    "content": "Copyright (C) 2009-2021 The Project Lombok Authors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n==============================================================================\nLicenses for included components:\n\norg.ow2.asm:asm\norg.ow2.asm:asm-analysis\norg.ow2.asm:asm-commons\norg.ow2.asm:asm-tree\norg.ow2.asm:asm-util\nASM: a very small and fast Java bytecode manipulation framework\n Copyright (c) 2000-2011 INRIA, France Telecom\n All rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n 1. Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n 3. Neither the name of the copyright holders nor the names of its\n    contributors may be used to endorse or promote products derived from\n    this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n THE POSSIBILITY OF SUCH DAMAGE.\n\n------------------------------------------------------------------------------\nrzwitserloot/com.zwitserloot.cmdreader \n \n Copyright © 2010 Reinier Zwitserloot.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n\n------------------------------------------------------------------------------\n\nprojectlombok/lombok.patcher\n\n Copyright (C) 2009-2021 The Project Lombok Authors.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n\n------------------------------------------------------------------------------"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/licenses/the mit license - mit.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t<link rel=\"profile\" href=\"http://gmpg.org/xfn/11\">\n\n\t<title>The MIT License &#8211; Open Source Initiative</title>\n<meta name='robots' content='max-image-preview:large' />\n<link rel='dns-prefetch' href='//stats.wp.com' />\n<link rel='dns-prefetch' href='//unpkg.com' />\n<link rel='preconnect' href='//i0.wp.com' />\n<link rel='preconnect' href='//c0.wp.com' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Feed\" href=\"https://opensource.org/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; Comments Feed\" href=\"https://opensource.org/comments/feed\" />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Open Source Initiative &raquo; The MIT License Comments Feed\" href=\"https://opensource.org/license/mit/feed\" />\n<link rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fmit\" />\n<link rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\" href=\"https://opensource.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fopensource.org%2Flicense%2Fmit&#038;format=xml\" />\n<style id='wp-img-auto-sizes-contain-inline-css' type='text/css'>\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id='osi_font_faces-inline-css' type='text/css'>\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: italic;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-vietnamese-100-900.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-latin-ext-100-900.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Libre Franklin';\n\tfont-style: normal;\n\tfont-weight: 100 900;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/libre-franklin/libre-franklin-100-900.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: italic;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold-italic.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Regular.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Regular.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 400;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Regular.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n/* vietnamese */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-vietnamese-Bold.woff2) format('woff2');\n\tunicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;\n}\n\n/* latin-ext */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-latin-ext-Bold.woff2) format('woff2');\n\tunicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n\n/* latin */\n@font-face {\n\tfont-family: 'Space Mono';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-display: swap;\n\tsrc: url(https://opensource.org/wp-content/themes/osi/assets/fonts/space-mono/space-mono-Bold.woff2) format('woff2');\n\tunicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/*# sourceURL=osi_font_faces-inline-css */\n</style>\n<style id='wp-emoji-styles-inline-css' type='text/css'>\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id='wp-block-library-inline-css' type='text/css'>\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style><style id='wp-block-buttons-inline-css' type='text/css'>\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/buttons/style.css */\n</style>\n<style id='wp-block-group-inline-css' type='text/css'>\n.wp-block-group{box-sizing:border-box}:where(.wp-block-group.wp-block-group-is-layout-constrained){position:relative}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/group/style.css */\n</style>\n<style id='wp-block-paragraph-inline-css' type='text/css'>\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/paragraph/style.css */\n</style>\n<style id='wp-block-button-inline-css' type='text/css'>\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://opensource.org/wp-content/plugins/gutenberg/build/styles/block-library/button/style.css */\n</style>\n<style id='global-styles-inline-css' type='text/css'>\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--brand-color-1: #3Ea638;--wp--preset--color--brand-color-2: #FB923c;--wp--preset--color--brand-color-3: #ebc342;--wp--preset--color--brand-color-4: #e66d4c;--wp--preset--color--brand-color-5: #1c531d;--wp--preset--color--brand-links: #23881f;--wp--preset--color--brand-alert: #a11c2e;--wp--preset--color--neutral-white: #ffffff;--wp--preset--color--neutral-lightest: #f2f3f2;--wp--preset--color--neutral-light: #d2d2d2;--wp--preset--color--neutral-mid: #949494;--wp--preset--color--neutral-dark: #414042;--wp--preset--color--neutral-darkest: #000000;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--brand-color-1-gradient-darker: linear-gradient(135deg,#3Ea638 0%,#1f531c 100%);--wp--preset--gradient--brand-color-1-gradient-lighter: linear-gradient(135deg,#3Ea638 0%,#63ff59 100%);--wp--preset--gradient--brand-color-2-gradient-darker: linear-gradient(135deg,#FB923c 0%,#7d491e 100%);--wp--preset--gradient--brand-color-2-gradient-lighter: linear-gradient(135deg,#FB923c 0%,#ffe960 100%);--wp--preset--gradient--brand-color-3-gradient-darker: linear-gradient(135deg,#ebc342 0%,#756121 100%);--wp--preset--gradient--brand-color-3-gradient-lighter: linear-gradient(135deg,#ebc342 0%,#ffff69 100%);--wp--preset--gradient--brand-color-4-gradient-darker: linear-gradient(135deg,#e66d4c 0%,#733626 100%);--wp--preset--gradient--brand-color-4-gradient-lighter: linear-gradient(135deg,#e66d4c 0%,#ffae79 100%);--wp--preset--gradient--brand-color-5-gradient-darker: linear-gradient(135deg,#1c531d 0%,#0e290e 100%);--wp--preset--gradient--brand-color-5-gradient-lighter: linear-gradient(135deg,#1c531d 0%,#2c842e 100%);--wp--preset--gradient--brand-color-1-brand-color-2-gradient: linear-gradient(135deg,#3Ea638 0%,#FB923c 100%);--wp--preset--gradient--brand-color-1-brand-color-3-gradient: linear-gradient(135deg,#3Ea638 0%,#ebc342 100%);--wp--preset--gradient--brand-color-1-brand-color-4-gradient: linear-gradient(135deg,#3Ea638 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-1-brand-color-5-gradient: linear-gradient(135deg,#3Ea638 0%,#1c531d 100%);--wp--preset--gradient--brand-color-2-brand-color-3-gradient: linear-gradient(135deg,#FB923c 0%,#ebc342 100%);--wp--preset--gradient--brand-color-2-brand-color-4-gradient: linear-gradient(135deg,#FB923c 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-2-brand-color-5-gradient: linear-gradient(135deg,#FB923c 0%,#1c531d 100%);--wp--preset--gradient--brand-color-3-brand-color-4-gradient: linear-gradient(135deg,#ebc342 0%,#e66d4c 100%);--wp--preset--gradient--brand-color-3-brand-color-5-gradient: linear-gradient(135deg,#ebc342 0%,#1c531d 100%);--wp--preset--gradient--brand-color-4-brand-color-5-gradient: linear-gradient(135deg,#e66d4c 0%,#1c531d 100%);--wp--preset--font-size--small: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.156), 16px);--wp--preset--font-size--medium: clamp(22px, 1.375rem + ((1vw - 3.2px) * 0.156), 24px);--wp--preset--font-size--large: clamp(24px, 1.5rem + ((1vw - 3.2px) * 0.313), 28px);--wp--preset--font-size--x-large: clamp(28px, 1.75rem + ((1vw - 3.2px) * 1.563), 48px);--wp--preset--font-size--x-small: clamp(12px, 0.75rem + ((1vw - 3.2px) * 1), 12px);--wp--preset--font-size--normal: clamp(16px, 1rem + ((1vw - 3.2px) * 0.313), 20px);--wp--preset--font-size--xx-large: clamp(30px, 1.875rem + ((1vw - 3.2px) * 2.656), 64px);--wp--preset--font-family--system-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--exo: \"Exo\", sans-serif;--wp--preset--font-family--albert-sans: \"Albert Sans\", sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--alignment--aligned-max-width: 50%;--wp--custom--animation--transition-duration: 0.3s;--wp--custom--animation--transition-type: all;--wp--custom--button--border--color: var(--wp--preset--color--brand-color-2);--wp--custom--button--border--color-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--border--radius: 50px;--wp--custom--button--border--style: solid;--wp--custom--button--border--width: 2px;--wp--custom--button--color--background: var(--wp--preset--color--brand-color-2);--wp--custom--button--color--background-secondary: var(--wp--preset--color--brand-color-1);--wp--custom--button--color--text: var(--wp--preset--color--neutral-dark);--wp--custom--button--hover--color--text: var(--wp--custom--color--foreground);--wp--custom--button--hover--color--background: var(--wp--custom--color--background);--wp--custom--button--hover--border--color: var(--wp--custom--color--foreground);--wp--custom--button--spacing--padding--top: 0.7em;--wp--custom--button--spacing--padding--bottom: 0.7em;--wp--custom--button--spacing--padding--left: 1.75em;--wp--custom--button--spacing--padding--right: 1.75em;--wp--custom--button--typography--font-family: var(--wp--custom--typography--body--font-family);--wp--custom--button--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--button--typography--font-weight: 600;--wp--custom--button--typography--line-height: 1;--wp--custom--color--foreground: var(--wp--custom--typography--body--color);--wp--custom--color--background: var(--wp--preset--color--white);--wp--custom--color--primary: var(--wp--preset--color--brand-color-1);--wp--custom--color--secondary: var(--wp--preset--color--brand-color-2);--wp--custom--color--tertiary: var(--wp--preset--color--brand-color-3);--wp--custom--form--padding: 15px;--wp--custom--form--border--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--border--radius: 0;--wp--custom--form--border--style: solid;--wp--custom--form--border--width: 1px;--wp--custom--form--checkbox--checked--content: \"\\2715\";--wp--custom--form--checkbox--checked--font-size: var(--wp--preset--font-size--x-small);--wp--custom--form--checkbox--checked--position--left: 3px;--wp--custom--form--checkbox--checked--position--top: 3px;--wp--custom--form--checkbox--checked--sizing--height: 12px;--wp--custom--form--checkbox--checked--sizing--width: 12px;--wp--custom--form--checkbox--unchecked--content: \"\";--wp--custom--form--checkbox--unchecked--position--left: 0;--wp--custom--form--checkbox--unchecked--position--top: 0.2em;--wp--custom--form--checkbox--unchecked--sizing--height: 16px;--wp--custom--form--checkbox--unchecked--sizing--width: 16px;--wp--custom--form--color--active: var(--wp--preset--color--brand-alert);--wp--custom--form--color--background: var(--wp--preset--color--neutral-white);--wp--custom--form--color--box-shadow: none;--wp--custom--form--label--spacing--margin--bottom: var(--wp--custom--gap--baseline);--wp--custom--form--label--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--label--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--form--label--typography--font-family: var(--wp--custom--typography--subheading--font-family);--wp--custom--form--label--typography--font-weight: normal;--wp--custom--form--label--typography--letter-spacing: normal;--wp--custom--form--label--typography--text-transform: none;--wp--custom--form--typography--color: var(--wp--preset--color--neutral-darkest);--wp--custom--form--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--form--placeholder--color: var(--wp--preset--color--neutral-dark);--wp--custom--form--placeholder--font-size: var(--wp--preset--font-size--small);--wp--custom--form--placeholder--style: italic;--wp--custom--gallery--caption--font-size: var(--wp--preset--font-size--small);--wp--custom--typography--body--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--body--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--body--font-weight: 400;--wp--custom--typography--body--font-weight-bold: 600;--wp--custom--typography--body--letter-space: 0;--wp--custom--typography--body--line-height: 1.6;--wp--custom--typography--body--margin: 20px;--wp--custom--typography--heading--color: var(--wp--preset--color--neutral-dark);--wp--custom--typography--heading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--heading--font-weight: 600;--wp--custom--typography--heading--font-weight-bold: 800;--wp--custom--typography--heading--letter-space: 0;--wp--custom--typography--heading--line-height: 1.125;--wp--custom--typography--heading--margin: 20px;--wp--custom--typography--subheading--color: var(--wp--preset--color--neutral-darkest);--wp--custom--typography--subheading--font-family: var(--wp--preset--font-family--libre-franklin);--wp--custom--typography--subheading--font-weight: 600;--wp--custom--typography--subheading--font-weight-bold: 800;--wp--custom--typography--subheading--letter-space: 0;--wp--custom--typography--subheading--line-height: 1.125;--wp--custom--latest-posts--meta--color--text: var(--wp--custom--color--primary);--wp--custom--layout--content-size: 730px;--wp--custom--layout--wide-size: 1180px;--wp--custom--layout--narrow-size: 600px;--wp--custom--layout--post-size: 920px;--wp--custom--layout--sidebar-width-small: 80%;--wp--custom--layout--sidebar-width-mid: 60%;--wp--custom--gap--baseline: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--vertical: min(var(--wp--custom--spacing--small-padding), 5vw);--wp--custom--gap--horizontal-sm: var(--wp--custom--spacing--small-padding);--wp--custom--gap--horizontal-md: var(--wp--custom--spacing--mid-padding);--wp--custom--gap--horizontal-lg: var(--wp--custom--spacing--max-padding);--wp--custom--gap--vertical-sm: var(--wp--custom--spacing--small-padding);--wp--custom--navigation--submenu--border--color: transparent;--wp--custom--navigation--submenu--border--radius: var(--wp--custom--form--border--radius);--wp--custom--navigation--submenu--border--style: var(--wp--custom--form--border--style);--wp--custom--navigation--submenu--border--width: var(--wp--custom--form--border--width);--wp--custom--navigation--submenu--color--background: var(--wp--custom--color--primary);--wp--custom--navigation--submenu--color--text: var(--wp--custom--color--background);--wp--custom--page--title--font-size: var(--wp--preset--font-size--x-large);--wp--custom--page--title--margin: 1.5em;--wp--custom--paragraph--dropcap--margin: .1em .1em 0 0;--wp--custom--paragraph--dropcap--typography--font-size: 110px;--wp--custom--paragraph--dropcap--typography--font-weight: 400;--wp--custom--paragraph--link--typography--text-decoration: underline;--wp--custom--paragraph--link--hover--typography--text-decoration: none;--wp--custom--paragraph--link--focus--typography--text-decoration: none;--wp--custom--post-author--typography--font-weight: normal;--wp--custom--post-comment--typography--font-size: var(--wp--preset--font-size--normal);--wp--custom--post-comment--typography--line-height: var(--wp--custom--typography--body--line-height);--wp--custom--pullquote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--pullquote--citation--typography--font-family: inherit;--wp--custom--pullquote--citation--typography--font-style: italic;--wp--custom--pullquote--citation--spacing--margin--top: var(--wp--custom--gap--vertical);--wp--custom--pullquote--typography--text-align: left;--wp--custom--pullquote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--citation--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--quote--citation--typography--font-style: italic;--wp--custom--quote--citation--typography--font-weight: 400;--wp--custom--quote--typography--text-align: left;--wp--custom--quote--typography--font-size: var(--wp--preset--font-size--medium);--wp--custom--quote--typography--line-height: 1.4;--wp--custom--separator--opacity: 1;--wp--custom--separator--width: 150px;--wp--custom--spacing--small-padding: 16px;--wp--custom--spacing--mid-padding: 32px;--wp--custom--spacing--max-padding: 48px;--wp--custom--table--border--color: ;--wp--custom--table--border--width: 1px;--wp--custom--table--border--style: solid;--wp--custom--table--color--background: transparent;--wp--custom--table--header--border--color: ;--wp--custom--table--header--border--width: 2px;--wp--custom--table--header--color--background: red;--wp--custom--table--typography--font-size: var(--wp--preset--font-size--small);--wp--custom--table--typography--line-height: 1.4;--wp--custom--table--figcaption--typography--font-size: var(--wp--preset--font-size--x-small);--wp--custom--table--spacing--cell-padding: .25em;--wp--custom--video--caption--text-align: center;--wp--custom--video--caption--margin: var(--wp--custom--gap--vertical) auto;}:root { --wp--style--global--content-size: var(--wp--custom--layout--content-size);--wp--style--global--wide-size: var(--wp--custom--layout--wide-size); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-color{color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-color{color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-color{color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-color{color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-color{color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-color{color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-color{color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-color{color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-color{color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-color{color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-color{color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-color{color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-color{color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-background-color{background-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-background-color{background-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-background-color{background-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-background-color{background-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-background-color{background-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-background-color{background-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-background-color{background-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-background-color{background-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-background-color{background-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-background-color{background-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-background-color{background-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-background-color{background-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-background-color{background-color: var(--wp--preset--color--neutral-darkest) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-brand-color-1-border-color{border-color: var(--wp--preset--color--brand-color-1) !important;}.has-brand-color-2-border-color{border-color: var(--wp--preset--color--brand-color-2) !important;}.has-brand-color-3-border-color{border-color: var(--wp--preset--color--brand-color-3) !important;}.has-brand-color-4-border-color{border-color: var(--wp--preset--color--brand-color-4) !important;}.has-brand-color-5-border-color{border-color: var(--wp--preset--color--brand-color-5) !important;}.has-brand-links-border-color{border-color: var(--wp--preset--color--brand-links) !important;}.has-brand-alert-border-color{border-color: var(--wp--preset--color--brand-alert) !important;}.has-neutral-white-border-color{border-color: var(--wp--preset--color--neutral-white) !important;}.has-neutral-lightest-border-color{border-color: var(--wp--preset--color--neutral-lightest) !important;}.has-neutral-light-border-color{border-color: var(--wp--preset--color--neutral-light) !important;}.has-neutral-mid-border-color{border-color: var(--wp--preset--color--neutral-mid) !important;}.has-neutral-dark-border-color{border-color: var(--wp--preset--color--neutral-dark) !important;}.has-neutral-darkest-border-color{border-color: var(--wp--preset--color--neutral-darkest) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-brand-color-1-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-darker) !important;}.has-brand-color-1-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-1-gradient-lighter) !important;}.has-brand-color-2-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-darker) !important;}.has-brand-color-2-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-2-gradient-lighter) !important;}.has-brand-color-3-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-darker) !important;}.has-brand-color-3-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-3-gradient-lighter) !important;}.has-brand-color-4-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-darker) !important;}.has-brand-color-4-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-4-gradient-lighter) !important;}.has-brand-color-5-gradient-darker-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-darker) !important;}.has-brand-color-5-gradient-lighter-gradient-background{background: var(--wp--preset--gradient--brand-color-5-gradient-lighter) !important;}.has-brand-color-1-brand-color-2-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-2-gradient) !important;}.has-brand-color-1-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-3-gradient) !important;}.has-brand-color-1-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-4-gradient) !important;}.has-brand-color-1-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-1-brand-color-5-gradient) !important;}.has-brand-color-2-brand-color-3-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-3-gradient) !important;}.has-brand-color-2-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-4-gradient) !important;}.has-brand-color-2-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-2-brand-color-5-gradient) !important;}.has-brand-color-3-brand-color-4-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-4-gradient) !important;}.has-brand-color-3-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-3-brand-color-5-gradient) !important;}.has-brand-color-4-brand-color-5-gradient-gradient-background{background: var(--wp--preset--gradient--brand-color-4-brand-color-5-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-x-small-font-size{font-size: var(--wp--preset--font-size--x-small) !important;}.has-normal-font-size{font-size: var(--wp--preset--font-size--normal) !important;}.has-xx-large-font-size{font-size: var(--wp--preset--font-size--xx-large) !important;}.has-system-font-font-family{font-family: var(--wp--preset--font-family--system-font) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-exo-font-family{font-family: var(--wp--preset--font-family--exo) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n\n<link rel='stylesheet' id='coblocks-extensions-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-extensions.css?ver=3.1.16' type='text/css' media='all' />\n<link rel='stylesheet' id='coblocks-animation-css' href='https://opensource.org/wp-content/plugins/coblocks/dist/style-coblocks-animation.css?ver=2677611078ee87eb3b1c' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-et-theme-css' href='https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/styles/theme/theme.scss.css?ver=1.0.0' type='text/css' media='all' />\n<link rel='stylesheet' id='contact-form-7-css' href='https://opensource.org/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.1.4' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-css' href='https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager.min.css?ver=7.2.2.1' type='text/css' media='all' />\n<link rel='stylesheet' id='events-manager-pro-css' href='https://opensource.org/wp-content/plugins/events-manager-pro/includes/css/events-manager-pro.css?ver=3.7.2.2' type='text/css' media='all' />\n<link rel='stylesheet' id='webmention-css' href='https://opensource.org/wp-content/plugins/webmention/assets/css/webmention.css?ver=5.5.0' type='text/css' media='all' />\n<link rel='stylesheet' id='comment_styles-css' href='https://opensource.org/wp-content/plugins/wp-discourse/lib/../css/comments.css?ver=1761590360' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-instant-search-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.chunk-main-payload.css?minify=false&#038;ver=a9cf87fff4e38de9bc98' type='text/css' media='all' />\n<link rel='stylesheet' id='cmplz-general-css' href='https://opensource.org/wp-content/plugins/complianz-gdpr/assets/css/cookieblocker.min.css?ver=1764787187' type='text/css' media='all' />\n<link rel='stylesheet' id='dashicons-css' href='https://c0.wp.com/c/6.9/wp-includes/css/dashicons.min.css' type='text/css' media='all' />\n<link rel='stylesheet' id='wp-components-css' href='https://opensource.org/wp-content/plugins/gutenberg/build/styles/components/style.css?ver=22.2.0' type='text/css' media='all' />\n<link rel='stylesheet' id='godaddy-styles-css' href='https://opensource.org/wp-content/plugins/coblocks/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' type='text/css' media='all' />\n<link rel='stylesheet' id='indieweb-css' href='https://opensource.org/wp-content/plugins/indieweb/static/css/indieweb.css?ver=4.0.5' type='text/css' media='all' />\n<link rel='stylesheet' id='osi-style-css' href='https://opensource.org/wp-content/themes/osi/style.css?ver=1750065233' type='text/css' media='all' />\n<style id='osi-style-inline-css' type='text/css'>\n:root{--wp--preset--color--brand-color-1:#3Ea638; --wp--preset--color--brand-color-1-darker:#2e7c2a; --wp--preset--color--brand-color-1-lighter:#4dcf46; --wp--preset--color--brand-color-2:#FB923c; --wp--preset--color--brand-color-2-darker:#bc6d2d; --wp--preset--color--brand-color-2-lighter:#ffb64b; --wp--preset--color--brand-color-3:#ebc342; --wp--preset--color--brand-color-3-darker:#b09231; --wp--preset--color--brand-color-3-lighter:#fff352; --wp--preset--color--brand-color-4:#e66d4c; --wp--preset--color--brand-color-4-darker:#ac5139; --wp--preset--color--brand-color-4-lighter:#ff885f; --wp--preset--color--brand-color-5:#1c531d; --wp--preset--color--brand-color-5-darker:#153e15; --wp--preset--color--brand-color-5-lighter:#236724; --wp--preset--color--brand-links:#23881f; --wp--preset--color--brand-links-darker:#1a6617; --wp--preset--color--brand-links-lighter:#2baa26; --wp--preset--color--brand-alert:#a11c2e; --wp--preset--color--brand-alert-darker:#781522; --wp--preset--color--brand-alert-lighter:#c92339; --wp--custom--color--brand-text:#000000; --wp--custom--color--brand-heading:#414042; --wp--custom--color--brand-subheading:#414042; --wp--preset--color--neutral-white:#ffffff; --wp--preset--color--neutral-lightest:#f2f3f2; --wp--preset--color--neutral-light:#d2d2d2; --wp--preset--color--neutral-mid:#949494; --wp--preset--color--neutral-dark:#414042; --wp--preset--color--neutral-darkest:#000000; }\n.has-brand-color-1-background-color{background-color:#3Ea638 !important;} .has-brand-color-1-color{color:#3Ea638 !important;} .has-brand-color-2-background-color{background-color:#FB923c !important;} .has-brand-color-2-color{color:#FB923c !important;} .has-brand-color-3-background-color{background-color:#ebc342 !important;} .has-brand-color-3-color{color:#ebc342 !important;} .has-brand-color-4-background-color{background-color:#e66d4c !important;} .has-brand-color-4-color{color:#e66d4c !important;} .has-brand-color-5-background-color{background-color:#1c531d !important;} .has-brand-color-5-color{color:#1c531d !important;} .has-brand-links-background-color{background-color:#23881f !important;} .has-brand-links-color{color:#23881f !important;} .has-brand-alert-background-color{background-color:#a11c2e !important;} .has-brand-alert-color{color:#a11c2e !important;} .has-brand-text-color{color:#000000 !important;} .has-brand-heading-color{color:#414042 !important;} .has-brand-subheading-color{color:#414042 !important;} .has-neutral-white-background-color{background-color:#ffffff !important;} .has-neutral-white-color{color:#ffffff !important;} .has-neutral-lightest-background-color{background-color:#f2f3f2 !important;} .has-neutral-lightest-color{color:#f2f3f2 !important;} .has-neutral-light-background-color{background-color:#d2d2d2 !important;} .has-neutral-light-color{color:#d2d2d2 !important;} .has-neutral-mid-background-color{background-color:#949494 !important;} .has-neutral-mid-color{color:#949494 !important;} .has-neutral-dark-background-color{background-color:#414042 !important;} .has-neutral-dark-color{color:#414042 !important;} .has-neutral-darkest-background-color{background-color:#000000 !important;} .has-neutral-darkest-color{color:#000000 !important;}\n:root{--wp--preset--gradient--brand-color-1-gradient-darker:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%); --wp--preset--gradient--brand-color-1-gradient-lighter:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%); --wp--preset--gradient--brand-color-1-brand-color-2-gradient:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%); --wp--preset--gradient--brand-color-1-brand-color-3-gradient:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%); --wp--preset--gradient--brand-color-1-brand-color-4-gradient:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-1-brand-color-5-gradient:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%); --wp--preset--gradient--brand-color-1-neutral-lightest-gradient:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-1-neutral-darkest-gradient:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-2-gradient-darker:linear-gradient(90deg,#FB923c 0%,#7d491e 100%); --wp--preset--gradient--brand-color-2-gradient-lighter:linear-gradient(90deg,#FB923c 0%,#ffe960 100%); --wp--preset--gradient--brand-color-2-brand-color-3-gradient:linear-gradient(90deg,#FB923c 0%,#ebc342 100%); --wp--preset--gradient--brand-color-2-brand-color-4-gradient:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-2-brand-color-5-gradient:linear-gradient(90deg,#FB923c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-2-neutral-lightest-gradient:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-2-neutral-darkest-gradient:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-3-gradient-darker:linear-gradient(90deg,#ebc342 0%,#756121 100%); --wp--preset--gradient--brand-color-3-gradient-lighter:linear-gradient(90deg,#ebc342 0%,#ffff69 100%); --wp--preset--gradient--brand-color-3-brand-color-4-gradient:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%); --wp--preset--gradient--brand-color-3-brand-color-5-gradient:linear-gradient(90deg,#ebc342 0%,#1c531d 100%); --wp--preset--gradient--brand-color-3-neutral-lightest-gradient:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-3-neutral-darkest-gradient:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-4-gradient-darker:linear-gradient(90deg,#e66d4c 0%,#733626 100%); --wp--preset--gradient--brand-color-4-gradient-lighter:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%); --wp--preset--gradient--brand-color-4-brand-color-5-gradient:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%); --wp--preset--gradient--brand-color-4-neutral-lightest-gradient:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-4-neutral-darkest-gradient:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%); --wp--preset--gradient--brand-color-5-gradient-darker:linear-gradient(90deg,#1c531d 0%,#0e290e 100%); --wp--preset--gradient--brand-color-5-gradient-lighter:linear-gradient(90deg,#1c531d 0%,#2c842e 100%); --wp--preset--gradient--brand-color-5-neutral-lightest-gradient:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%); --wp--preset--gradient--brand-color-5-neutral-darkest-gradient:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-lightest-gradient-darker:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%); --wp--preset--gradient--neutral-lightest-gradient-lighter:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%); --wp--preset--gradient--neutral-lightest-neutral-darkest-gradient:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%); --wp--preset--gradient--neutral-darkest-gradient-darker:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%); --wp--preset--gradient--neutral-darkest-gradient-lighter:linear-gradient(90deg,#1e1e1e 0%,#303030 100%); }\n.has-brand-color-1-gradient-darker-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1f531c 100%) !important;} .has-brand-color-1-gradient-lighter-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#63ff59 100%) !important;} .has-brand-color-1-brand-color-2-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#FB923c 100%) !important;} .has-brand-color-1-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#ebc342 100%) !important;} .has-brand-color-1-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#e66d4c 100%) !important;} .has-brand-color-1-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1c531d 100%) !important;} .has-brand-color-1-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#f4f2f4 100%) !important;} .has-brand-color-1-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#3Ea638 0%,#1e1e1e 100%) !important;} .has-brand-color-2-gradient-darker-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#7d491e 100%) !important;} .has-brand-color-2-gradient-lighter-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ffe960 100%) !important;} .has-brand-color-2-brand-color-3-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#ebc342 100%) !important;} .has-brand-color-2-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#e66d4c 100%) !important;} .has-brand-color-2-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1c531d 100%) !important;} .has-brand-color-2-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#f4f2f4 100%) !important;} .has-brand-color-2-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#FB923c 0%,#1e1e1e 100%) !important;} .has-brand-color-3-gradient-darker-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#756121 100%) !important;} .has-brand-color-3-gradient-lighter-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#ffff69 100%) !important;} .has-brand-color-3-brand-color-4-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#e66d4c 100%) !important;} .has-brand-color-3-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1c531d 100%) !important;} .has-brand-color-3-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#f4f2f4 100%) !important;} .has-brand-color-3-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#ebc342 0%,#1e1e1e 100%) !important;} .has-brand-color-4-gradient-darker-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#733626 100%) !important;} .has-brand-color-4-gradient-lighter-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#ffae79 100%) !important;} .has-brand-color-4-brand-color-5-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1c531d 100%) !important;} .has-brand-color-4-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#f4f2f4 100%) !important;} .has-brand-color-4-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#e66d4c 0%,#1e1e1e 100%) !important;} .has-brand-color-5-gradient-darker-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#0e290e 100%) !important;} .has-brand-color-5-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#2c842e 100%) !important;} .has-brand-color-5-neutral-lightest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#f4f2f4 100%) !important;} .has-brand-color-5-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#1c531d 0%,#1e1e1e 100%) !important;} .has-neutral-lightest-gradient-darker-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#7a797a 100%) !important;} .has-neutral-lightest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#ffffff 100%) !important;} .has-neutral-lightest-neutral-darkest-gradient-gradient-background{background:linear-gradient(90deg,#f4f2f4 0%,#1e1e1e 100%) !important;} .has-neutral-darkest-gradient-darker-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#0f0f0f 100%) !important;} .has-neutral-darkest-gradient-lighter-gradient-background{background:linear-gradient(90deg,#1e1e1e 0%,#303030 100%) !important;}\n:root{--wp--custom--color--brand-color-1-rgb:62, 166, 56; --wp--custom--color--brand-color-2-rgb:251, 146, 60; --wp--custom--color--brand-color-3-rgb:235, 195, 66; --wp--custom--color--brand-color-4-rgb:230, 109, 76; --wp--custom--color--brand-color-5-rgb:28, 83, 29; --wp--custom--color--neutral-white-rgb:255, 255, 255; --wp--custom--color--neutral-lightest-rgb:242, 243, 242; --wp--custom--color--neutral-light-rgb:210, 210, 210; --wp--custom--color--neutral-mid-rgb:148, 148, 148; --wp--custom--color--neutral-dark-rgb:65, 64, 66; --wp--custom--color--neutral-darkest-rgb:0, 0, 0; }\n\n.wp-block-cover { background-image:url()}\n/*# sourceURL=osi-style-inline-css */\n</style>\n<link rel='stylesheet' id='swiper-css-css' href='https://unpkg.com/swiper@11/swiper-bundle.min.css?ver=1750065233' type='text/css' media='all' />\n<link rel='stylesheet' id='genericons-css' href='https://c0.wp.com/p/jetpack/15.3.1/_inc/genericons/genericons/genericons.css' type='text/css' media='all' />\n<link rel='stylesheet' id='jetpack-social-menu-css' href='https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-classic-theme-helper/src/social-menu/social-menu.css?ver=1.0' type='text/css' media='all' />\n<link rel='stylesheet' id='syndication-style-css' href='https://opensource.org/wp-content/plugins/syndication-links/css/syn.min.css?ver=4.5.3' type='text/css' media='all' />\n<script type=\"text/javascript\" id=\"altcha-widget-custom-options-js-after\">\n/* <![CDATA[ */\n(() => { window.ALTCHA_WIDGET_ATTRS = {\"challengeurl\":\"https:\\/\\/opensource.org\\/wp-json\\/altcha\\/v1\\/challenge\",\"strings\":\"{\\\"error\\\":\\\"Verification failed. Try again later.\\\",\\\"footer\\\":\\\"Protected by <a href=\\\\\\\"https:\\\\\\/\\\\\\/altcha.org\\\\\\/\\\\\\\" target=\\\\\\\"_blank\\\\\\\">ALTCHA<\\\\\\/a>\\\",\\\"label\\\":\\\"I'm not a robot\\\",\\\"verified\\\":\\\"Verified\\\",\\\"verifying\\\":\\\"Verifying...\\\",\\\"waitAlert\\\":\\\"Verifying... please wait.\\\"}\",\"hidefooter\":\"1\"}; })();\n//# sourceURL=altcha-widget-custom-options-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery.min.js\" id=\"jquery-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/jquery-migrate.min.js\" id=\"jquery-migrate-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/core.min.js\" id=\"jquery-ui-core-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/mouse.min.js\" id=\"jquery-ui-mouse-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/sortable.min.js\" id=\"jquery-ui-sortable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/datepicker.min.js\" id=\"jquery-ui-datepicker-js\"></script>\n<script type=\"text/javascript\" id=\"jquery-ui-datepicker-js-after\">\n/* <![CDATA[ */\njQuery(function(jQuery){jQuery.datepicker.setDefaults({\"closeText\":\"Close\",\"currentText\":\"Today\",\"monthNames\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\"monthNamesShort\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"nextText\":\"Next\",\"prevText\":\"Previous\",\"dayNames\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],\"dayNamesShort\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"dayNamesMin\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"dateFormat\":\"MM d, yy\",\"firstDay\":1,\"isRTL\":false});});\n//# sourceURL=jquery-ui-datepicker-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/resizable.min.js\" id=\"jquery-ui-resizable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/draggable.min.js\" id=\"jquery-ui-draggable-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/controlgroup.min.js\" id=\"jquery-ui-controlgroup-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/checkboxradio.min.js\" id=\"jquery-ui-checkboxradio-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/button.min.js\" id=\"jquery-ui-button-js\"></script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/jquery/ui/dialog.min.js\" id=\"jquery-ui-dialog-js\"></script>\n<script type=\"text/javascript\" id=\"events-manager-js-extra\">\n/* <![CDATA[ */\nvar EM = {\"ajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"locationajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php?action=locations_search\",\"firstDay\":\"1\",\"locale\":\"en\",\"dateFormat\":\"yy-mm-dd\",\"ui_css\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/jquery-ui/build.min.css\",\"show24hours\":\"0\",\"is_ssl\":\"1\",\"autocomplete_limit\":\"10\",\"calendar\":{\"breakpoints\":{\"small\":560,\"medium\":908,\"large\":false}},\"phone\":\"\",\"datepicker\":{\"format\":\"Y-m-d\"},\"search\":{\"breakpoints\":{\"small\":650,\"medium\":850,\"full\":false}},\"url\":\"https://opensource.org/wp-content/plugins/events-manager\",\"assets\":{\"input.em-uploader\":{\"js\":{\"em-uploader\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/em-uploader.js?v=7.2.2.1\",\"event\":\"em_uploader_ready\"}}},\".em-event-editor\":{\"js\":{\"event-editor\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager-event-editor.js?v=7.2.2.1\",\"event\":\"em_event_editor_ready\"}},\"css\":{\"event-editor\":\"https://opensource.org/wp-content/plugins/events-manager/includes/css/events-manager-event-editor.min.css?v=7.2.2.1\"}},\".em-recurrence-sets, .em-timezone\":{\"js\":{\"luxon\":{\"url\":\"luxon/luxon.js?v=7.2.2.1\",\"event\":\"em_luxon_ready\"}}},\".em-booking-form, #em-booking-form, .em-booking-recurring, .em-event-booking-form\":{\"js\":{\"em-bookings\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/bookingsform.js?v=7.2.2.1\",\"event\":\"em_booking_form_js_loaded\"},\"em-coupons\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.js?v=3.7.2.2\",\"requires\":\"em-bookings\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}},\"#em-opt-archetypes\":{\"js\":{\"archetypes\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetype-editor.js?v=7.2.2.1\",\"archetypes_ms\":\"https://opensource.org/wp-content/plugins/events-manager/includes/js/admin-archetypes.js?v=7.2.2.1\",\"qs\":\"qs/qs.js?v=7.2.2.1\"}},\".em-cart-coupons-form\":{\"js\":{\"em-coupons-cart\":{\"url\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons-cart.js?v=3.7.2.2\",\"event\":\"em_timepicker_ready\"}},\"css\":{\"em-coupons\":\"https://opensource.org/wp-content/plugins/events-manager-pro/add-ons/coupons/coupons.min.css?v=3.7.2.2\"}}},\"cached\":\"1\",\"bookingInProgress\":\"Please wait while the booking is being submitted.\",\"tickets_save\":\"Save Ticket\",\"bookingajaxurl\":\"https://opensource.org/wp-admin/admin-ajax.php\",\"bookings_export_save\":\"Export Bookings\",\"bookings_settings_save\":\"Save Settings\",\"booking_delete\":\"Are you sure you want to delete?\",\"booking_offset\":\"30\",\"bookings\":{\"submit_button\":{\"text\":{\"default\":\"Submit Booking\",\"free\":\"Submit Booking\",\"payment\":\"Submit Booking - %s\",\"processing\":\"Processing ...\"}},\"update_listener\":\"\"},\"bb_full\":\"Sold Out\",\"bb_book\":\"Book Now\",\"bb_booking\":\"Booking...\",\"bb_booked\":\"Booking Submitted\",\"bb_error\":\"Booking Error. Try again?\",\"bb_cancel\":\"Cancel\",\"bb_canceling\":\"Canceling...\",\"bb_cancelled\":\"Cancelled\",\"bb_cancel_error\":\"Cancellation Error. Try again?\",\"txt_search\":\"Search\",\"txt_searching\":\"Searching...\",\"txt_loading\":\"Loading...\",\"cache\":\"1\",\"api_nonce\":\"7d68a4059a\",\"attendance_api_url\":\"https://opensource.org/wp-json/events-manager/v1/attendance\"};\n//# sourceURL=events-manager-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager/includes/js/events-manager.js?ver=7.2.2.1\" id=\"events-manager-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/events-manager-pro/includes/js/events-manager-pro.js?ver=3.7.2.2\" id=\"events-manager-pro-js\"></script>\n<script type=\"text/javascript\" defer data-domain='opensource.org' data-api='https://opensource.org/wp-json/fcdf21/v1/da74/d08e74b0' data-cfasync='false' src=\"//opensource.org/wp-content/uploads/4931f7089c/801c1c58.js?ver=1765046500\" id=\"plausible-analytics-js\"></script>\n<script type=\"text/javascript\" id=\"plausible-analytics-js-after\">\n/* <![CDATA[ */\nwindow.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }\n//# sourceURL=plausible-analytics-js-after\n/* ]]> */\n</script>\n<link rel=\"https://api.w.org/\" href=\"https://opensource.org/wp-json/\" /><link rel=\"alternate\" title=\"JSON\" type=\"application/json\" href=\"https://opensource.org/wp-json/wp/v2/license/395\" /><link rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"https://opensource.org/xmlrpc.php?rsd\" />\n\n<link rel=\"canonical\" href=\"https://opensource.org/license/mit\" />\n<link rel='shortlink' href='https://opensource.org/?p=395' />\n\n\n\t<!-- Nelio Content -->\n\t<meta property=\"og:locale\" content=\"en_US\" />\n\t<meta property=\"og:type\" content=\"article\" />\n\t<meta property=\"og:title\" content=\"The MIT License\" />\n\t<meta property=\"og:description\" content=\"Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt; Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8220;Software&#8221;), to deal in the…\" />\n\t<meta property=\"og:url\" content=\"https://opensource.org/license/MIT/\" />\n\t<meta property=\"og:site_name\" content=\"Open Source Initiative\" />\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<!-- /Nelio Content -->\n\n\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Podcast RSS feed\" href=\"https://opensource.org/feed/podcast/deep-dive-ai/\" />\n\n\t<style>img#wpstats{display:none}</style>\n\t\t\t\t\t<style>.cmplz-hidden {\n\t\t\t\t\tdisplay: none !important;\n\t\t\t\t}</style><style type=\"text/css\">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>\t\t<style type=\"text/css\">\n\t\t\t\t\t.site-title,\n\t\t\t.site-description {\n\t\t\t\tposition: absolute;\n\t\t\t\tclip: rect(1px, 1px, 1px, 1px);\n\t\t\t}\n\t\t\t\t\t</style>\n\t\t<style class='wp-fonts-local' type='text/css'>\n@font-face{font-family:Exo;font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/Exo/Exo-VariableFont_wght.ttf') format('truetype');}\n@font-face{font-family:\"Albert Sans\";font-style:normal;font-weight:100 200 300 400 500 600 700 800 900;font-display:fallback;src:url('https://opensource.org/wp-content/themes/osi/assets/fonts/albert-sans/AlbertSans-VariableFont_wght.ttf') format('truetype');}\n</style>\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=32%2C32&#038;quality=80&#038;ssl=1\" sizes=\"32x32\" />\n<link rel=\"icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=192%2C192&#038;quality=80&#038;ssl=1\" sizes=\"192x192\" />\n<link rel=\"apple-touch-icon\" href=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=180%2C180&#038;quality=80&#038;ssl=1\" />\n<meta name=\"msapplication-TileImage\" content=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/01/cropped-cropped-OSI_Horizontal_Logo_0-e1674081292667.png?fit=270%2C270&#038;quality=80&#038;ssl=1\" />\n\t\t<style type=\"text/css\" id=\"wp-custom-css\">\n\t\t\t\n/*prev*/\n\n.jetpack-instant-search__search-results-search-form {\n\tmax-width: 100%;\n}\n\n/* Prevents image duplication of featured images on Event pages. */\n.single-event .em-item-image {\n    display: none;\n}\n\n/* Keep other existing styles */\n.single-sc_event .entry-header .entry-title {\n\tdisplay: none;\n}\n\n/* Carrie's design tweaks - 11.2.22 */\n\n.wp-block-coblocks-posts__content a {\n\tline-height: 1.3em;\n}\n.wp-block-post-title {\n\tmargin-bottom: 1em;\n}\n.entry-content h2 {\n\tfont-size:2rem;\n\tfont-weight:700;\n\tmargin-top: 2em;\n} \n.entry-content h3 {\n\tcolor: #414042;\n\tfont-size:1.5rem;\n\tfont-weight: 700;\n\tmargin-top: 2em;\n}\n.wp-block-media-text__content h2, .wp-block-media-text__content h3 {\n\tmargin-top:0;\n}\np {\n\tmargin-bottom: 1.5em;\n}\n li {\n\t margin-bottom:1em;\n}\nblockquote > *, .wp-block-pullquote > *, .wp-block-quote > * {\n    font-style: italic;\n    font-size: 18px;\n    font-weight: 200;\n    line-height: 1.8rem;\n    margin: 2em 0 2rem 0!important;\n}\n\n/*to address centering a single post if there is only one */\n.wp-block-post-template.is-flex-container li {\n\tmargin: 0 auto;\n}\n\n/* Remove when the changes to prod are live 2-13-2023 */\n\n\n@media only screen and (min-width: 1200px) {\n\t.header--blog-name img {\n\tmargin-top: -16px;\n\t}\n\t\t\n\t\t.nav-main--menu > li:hover > ul, .nav-main--menu > li:focus > ul, .nav-main--menu li.tab-active > ul {\n\t\t\tmax-height: 800px;\n\t\t}\n\t\n}\n\n/* \n * Styles for OSI Sponsors Block    */\n.block-editor-block-list__block.wp-block-osi-sponsors-list {\n    padding-top: 1.2rem;\n}\n.osi-partner-logo {\n    margin: 4rem 0;\n    max-width: 230px;\n}\n\n.osi-sponsor-logo img {\n\twidth: auto;\n}\n\n/* 2023-08-25 DR hide $0.00 per ticket */\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price {\n\tfont-size: 0;\n}\n\n.postid-8996.single-sc_event .content.has_no_sidebar .content--page article:not(.archive) .sc-event-ticketing-price::after {\n\tcontent: \"Free Ticket\";\n\tvisibility: visible;\n\tmargin: 22px auto 0;\n\tfont-size: 1.25rem;\n\t\n}\n#sc_event_details_8996 {\n\tdisplay: none\n}\n\n#pre-footer .wpcf7 input {\n\twidth: 50%;\n\tborder-radius: 0;\n\tdisplay:inline-block;\n\tborder: 2px black solid;\n\tmargin:0;\n\tpadding: 15px 20px;\n} \n#pre-footer .wpcf7 input[type=submit] {\n\tbackground-color:black;\n\tcolor:white;\n}\n\n.sidebar-post-loop ul li .wp-block-post-date {\n\tmargin-top: 0;\n}\n.sidebar-post-loop ul li.wp-block-post {\n\tborder-bottom  : #CECECE 1px solid;\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tmargin-top     : 0;\n}\n\n.sidebar-post-loop ul li.wp-block-post:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-post-title.wp-block-post-title {\n\tmargin-bottom : 3px !important;\n\tmargin-top    : 0;\n\tfont-family   : 'Poppins', sans-serif;\n}\n\n.sidebar-comment-posts {\n\tpadding-left : 0;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment {\n\tpadding-bottom : 10px;\n\tmargin-bottom  : 10px;\n\tborder-bottom  : #CECECE 1px solid;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tfont-size   : 16px;\n\tline-height : 24px;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt {\n\tfont-size   : 16px;\n\tline-height : 28px;\n\tcolor       : #767676;\n}\n\n.sidebar-comment-posts li.wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt p {\n\tmargin-bottom : 0;\n}\n\n.sidebar-terms {\n\tpadding-left : 0;\n\tmargin-left: 0;\n\tlist-style   : none;\n}\n\n.sidebar-terms li {\n\tborder-bottom : 1px solid #CECECE;\n\tpadding       : 0 0 10px;\n\tmargin        : 0 0 10px;\n}\n\n.sidebar-terms li:last-child {\n\tborder-bottom : none;\n}\n\n.sidebar-terms li a {\n\ttext-decoration : none;\n\tcolor           : #3DA639;\n}\n\n.main-post-loop ul li .wp-block-post-featured-image {\n\tborder : 1px solid #E1E1E1;\n}\n\n#more-blog-link a {\n\ttext-decoration : none;\n\tcolor           : #1D1D1D;\n}\n\n#wp--skip-link--target {\n\tmargin-top : 24px !important;\n}\n\n.blog-page--main-post-query .wp-block-post-featured-image {\n\tmargin-bottom: 15px;\n}\n\n\n.page-id-9688 .content--page .entry-header {\n\tdisplay: none;\n}\n\n.special-sep {\n\tposition : relative;\n}\n\n.special-sep:before {\n\tcontent          : '';\n\ttop              : 50%;\n\tleft             : 0;\n\tright            : 0;\n\theight           : 1px;\n\tcontent          : '';\n\tposition         : absolute;\n\tbackground-color : #000000;\n}\n\n.special-sep:after {\n\tcontent    : '';\n\tleft       : calc(50% - 80px / 2);\n\twidth      : 80px;\n\tcontent    : '';\n\tposition   : absolute;\n\theight     : 40px;\n\ttop        : -20px;\n\tbackground : #FFFFFF url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4yIiBiYXNlUHJvZmlsZT0idGlueSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjU2IDI0OCIgb3ZlcmZsb3c9InZpc2libGUiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZmlsbD0ibm9uZSIgZD0iTTE0My4zLDE2Ny44YzIyLjEtOC41LDMzLjEtMzMuMiwyNC42LTU1LjNjLTguNS0yMi4xLTMzLjItMzMuMS01NS4zLTI0LjZjLTIyLjEsOC41LTMzLjEsMzMuMi0yNC42LDU1LjMNCgkJYzQuMywxMS4zLDEzLjMsMjAuMywyNC42LDI0LjZMODMuOCwyNDNjLTYzLjUtMjQuNC05NS4zLTk1LjctNzAuOS0xNTkuM3M5NS43LTk1LjMsMTU5LjItNzAuOXM5NS4zLDk1LjcsNzAuOSwxNTkuMw0KCQljLTEyLjUsMzIuNi0zOC4zLDU4LjQtNzAuOSw3MC45TDE0My4zLDE2Ny44Ii8+DQoJPHBhdGggZmlsbD0iIzIzMUYyMCIgZD0iTTE3Mi4yLDI0Ny40Yy0wLjYsMC0xLjItMC4xLTEuOC0wLjRjLTEuMS0wLjUtMS45LTEuNC0yLjMtMi40bC0yOC44LTc1LjFjLTAuOS0yLjMsMC4zLTQuOCwyLjUtNS43DQoJCWM5LjYtMy43LDE3LjEtMTAuOSwyMS4zLTIwLjJjNC4yLTkuNCw0LjQtMTkuOCwwLjgtMjkuNEMxNTYuMiw5NC4zLDEzNCw4NC40LDExNC4yLDkyYy0xOS44LDcuNi0yOS43LDI5LjgtMjIuMSw0OS42DQoJCWMzLjksMTAuMSwxMS45LDE4LjIsMjIuMSwyMi4xYzIuMywwLjksMy40LDMuNCwyLjUsNS43TDg4LDI0NC41Yy0wLjQsMS4xLTEuMywyLTIuMywyLjRjLTEuMSwwLjUtMi4zLDAuNS0zLjQsMC4xDQoJCWMtMzEuOC0xMi4yLTU3LTM2LjEtNzAuOS02Ny4zQy0yLjUsMTQ4LjctMy40LDExNCw4LjgsODIuMXMzNi4xLTU3LDY3LjItNzAuOXM2NS44LTE0LjgsOTcuNy0yLjZzNTcsMzYuMSw3MC45LDY3LjMNCgkJYzEzLjksMzEuMiwxNC44LDY1LjgsMi42LDk3LjdjLTEyLjksMzMuNy0zOS43LDYwLjUtNzMuNCw3My41QzE3My4yLDI0Ny4zLDE3Mi43LDI0Ny40LDE3Mi4yLDI0Ny40TDE3Mi4yLDI0Ny40eiBNMTI4LDguOQ0KCQljLTE2LjQsMC0zMi44LDMuNS00OC4zLDEwLjNjLTI5LDEyLjktNTEuMiwzNi40LTYyLjYsNjZjLTExLjQsMjkuNi0xMC41LDYxLjksMi40LDkwLjljMTIuMywyNy43LDM0LjIsNDkuMiw2MS45LDYxbDI1LjctNjcNCgkJYy0xMC42LTUuMi0xOC45LTE0LjMtMjMuMS0yNS40Yy05LjMtMjQuMywyLjktNTEuNywyNy4xLTYxYzI0LjMtOS4zLDUxLjYsMi45LDYxLDI3LjJjNC41LDExLjgsNC4yLDI0LjYtMC45LDM2LjENCgkJYy00LjUsMTAuMi0xMi4zLDE4LjItMjIuMiwyMy4xbDI1LjcsNjdjMjkuNS0xMi42LDUyLjgtMzYuNyw2NC4zLTY2LjdjMTEuNC0yOS42LDEwLjUtNjEuOS0yLjQtOTAuOWMtMTIuOS0yOS0zNi4zLTUxLjItNjYtNjIuNg0KCQlDMTU2LjgsMTEuNiwxNDIuMyw4LjksMTI4LDguOUwxMjgsOC45eiIvPg0KCTxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik0yMzcuNywyMjkuNmMtMS42LDEuNi0yLjQsMy42LTIuNCw1LjhjMCwyLjMsMC44LDQuMywyLjQsNS45YzEuNiwxLjYsMy42LDIuNCw1LjksMi40DQoJCWMyLjMsMCw0LjItMC44LDUuOC0yLjRjMS42LTEuNiwyLjQtMy42LDIuNC01LjljMC0yLjItMC44LTQuMi0yLjQtNS44Yy0xLjYtMS43LTMuNi0yLjUtNS45LTIuNQ0KCQlDMjQxLjMsMjI3LjEsMjM5LjQsMjI3LjksMjM3LjcsMjI5LjZMMjM3LjcsMjI5LjZ6IE0yNTAuNywyNDIuNmMtMiwxLjktNC4zLDIuOC03LDIuOGMtMi44LDAtNS4yLTEtNy4xLTIuOQ0KCQljLTEuOS0xLjktMi45LTQuMy0yLjktNy4xYzAtMi45LDEtNS40LDMuMS03LjNjMi0xLjgsNC4zLTIuNyw2LjktMi43YzIuOCwwLDUuMSwxLDcuMSwyLjljMiwyLDIuOSw0LjMsMi45LDcuMQ0KCQlDMjUzLjcsMjM4LjIsMjUyLjcsMjQwLjYsMjUwLjcsMjQyLjZMMjUwLjcsMjQyLjZ6IE0yNDQuNSwyMzJjLTAuNC0wLjItMS0wLjItMS43LTAuMmgtMC43djMuMmgxLjFjMC43LDAsMS4yLTAuMSwxLjYtMC40DQoJCXMwLjYtMC43LDAuNi0xLjNDMjQ1LjQsMjMyLjcsMjQ1LjEsMjMyLjMsMjQ0LjUsMjMyTDI0NC41LDIzMnogTTIzOS4yLDI0MXYtMTEuMWMwLjcsMCwxLjcsMCwzLjEsMHMyLjEsMCwyLjMsMA0KCQljMC45LDAuMSwxLjYsMC4zLDIuMiwwLjZjMSwwLjUsMS41LDEuNCwxLjUsMi43YzAsMC45LTAuMywxLjYtMC44LDJjLTAuNSwwLjQtMS4yLDAuNy0xLjksMC43YzAuNywwLjEsMS4yLDAuNCwxLjYsMC42DQoJCWMwLjcsMC41LDEsMS40LDEsMi41djFjMCwwLjEsMCwwLjIsMCwwLjNzMCwwLjIsMC4xLDAuM2wwLjEsMC4zaC0yLjhjLTAuMS0wLjQtMC4xLTAuOS0wLjItMS41cy0wLjEtMS4xLTAuMi0xLjQNCgkJYy0wLjEtMC40LTAuNC0wLjctMC44LTAuOGMtMC4yLTAuMS0wLjUtMC4xLTEtMC4ybC0wLjYsMGgtMC42djMuOUgyMzkuMnoiLz4NCjwvZz4NCjwvc3ZnPg0K) no-repeat center;\n}\n\n.sidebar .wp-block-latest-posts__list li,\n.sidebar .wp-block-latest-comments li {\n\tborder-bottom: #CECECE 1px solid;\n  padding-bottom: 10px;\n  margin-bottom: 10px;\n  margin-top: 0;\n}\n.sidebar .wp-block-latest-posts__post-date, \n.sidebar .wp-block-post-date, .wp-block-coblocks-posts__date {\n\tcolor: #1d1d1d;\n  font-size: 13px;\n\tfont-weight: 400;\n\tmargin: 0;\n}\n.sidebar .wp-block-latest-comments__comment-excerpt p,\n.sidebar .wp-block-latest-comments__comment-excerpt,\n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-excerpt, \n.sidebar .has-avatars .wp-block-latest-comments__comment .wp-block-latest-comments__comment-meta {\n\tmargin: 0;\n\tmargin-left:0;\n\tline-height: 28px;\n  color: #767676;\n}\n\n.blog .sidebar .wp-block-latest-posts__post-title,\n.blog .sidebar .wp-block-latest-comments__comment-link,\n.blog .sidebar .wp-block-latest-comments__comment-author,\n.blog .sidebar .widget_top-posts a,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_categories,\n.blog .sidebar #recentcomments {\n\tfont-weight: 400;\n\tcolor: #3da639;\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.217), 16px);\n    line-height: 1.8;\n\ttext-decoration: none;\n}\n.blog .sidebar h2 {\n\tfont-size: clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.435), 18px);\n}\n\n\n.blog .sidebar .wp-block-categories-list,\n.blog .sidebar .widget_top-posts ul,\n.blog .sidebar .widget_recent_entries,\n.blog .sidebar .widget_recent_entries ul,\n.blog .sidebar #recentcomments,\n.blog .sidebar .widget_categories ul {\n\tpadding-left: 0px;\n}\n.blog .sidebar .wp-block-categories-list li,\n.blog .sidebar .widget_top-posts li,\n.blog .sidebar .widget_recent_entries li,\n.blog .sidebar #recentcomments li,\n.blog .sidebar .widget_categories li {\n\tlist-style: none;\n\tborder-bottom: 1px solid #CECECE;\n    padding: 0 0 10px;\n    margin: 0 0 10px;\n}\n\n.blog .sidebar .wp-block-categories-list li a,\n.blog .sidebar .widget_recent_entries a,\n.blog .sidebar .widget_categories a,\n.blog .sidebar #recentcomments a {\n\ttext-decoration: none;\n\tcursor: pointer !important;\n\tline-height: 1.8;\n\tfont-weight: 400;\n\tcolor: #3da639;\n}\n\n#comments ul.reaction-list {\n\tlist-style-type: none;\n}\n#comments ul.reaction-list li {\n\tdisplay:inline-block;\n\tpadding:0;\n\tmargin:0\n}\n#comments ul.reaction-list li .hide-name,\n#comments ul.reaction-list li .emoji-overlay {\n\tdisplay:none;\n}\n#comments ul.reaction-list li img {\n\twidth:50px;\n\tmax-width: auto;\n}\n#comments ul.reaction-list li a.customize-unpreviewable {\n\tcursor:pointer !important;\n\tdisplay:inline-block;\n}\n.comment-body {\n\twidth: auto;\n}\n.email-block-wrap {\n\tdisplay:block;\n\twidth:100%;\n\tclear:both;\n\tmargin-bottom: -5em;\n\tz-index: 0;\n\tposition: relative;\n}\n.email-block-wrap input {\n\twidth: 50%;\n\tdisplay:inline-block;\n\tborder-radius: 0;\n\tborder: 2px black solid;\n\theight: 60px;\n}\n.email-block-wrap span.wpcf7-not-valid-tip {\n\tposition: absolute;\n\tcolor:white\n}\n.email-block-wrap span.wpcf7-form-control-wrap {\n\tdisplay: inline-block;\n\twidth:50%;\n}\n.email-block-wrap span.wpcf7-form-control-wrap input {\n\twidth:100%;\n}\n.email-block-wrap input[type=submit] {\n\tbackground: black;\n\tcolor:white;\n}\n.email-block-wrap input[type=submit]:hover {\n\tbackground: white;\n\tcolor: black;\n}\n.email-block-wrap input:hover,\n.email-block-wrap form.customize-unpreviewable input:hover,\nform.customize-unpreviewable {\n\tcursor:pointer !important;\n}\n.email-block-wrap input[type=email]:hover,\n.email-block-wrap form.customize-unpreviewable input[type=email]:hover {\n\tcursor:text !important;\n}\n.email-block-wrap .wpcf7-response-output {\n\tborder-color:white !important;\n\tmargin:10px 0 !important;\n\tcolor:white;\n}\n.email-block-wrap form p {\n\tmargin: 0;\n\tpadding: 0;\n}\n.email-block-wrap {\n\tz-index: 1;\n\tborder-bottom: 1px solid black;\n}\n.footer-cta {\n    z-index: 2;\n    position: relative;\n}\n.footer-main {\n\tpadding-top: 50px\n}\n.blog .first-post .post--byline {\n\tfont-size:clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n\tcolor: gray;\n}\n.blog .first-post .entry-header ul {\n\tlist-style-type: none;\n\tpadding-left: 0;\n\tmargin-left: 0;\n\tfont-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n\n.blog .first-post h2 {\n\tfont-size: 35px;\n\tline-height: 45px;\n}\n\n.blog .content .content-full .content--page {\n    max-width: 1550px;\n}\n.syndication-links {\n\t  margin-top: 0;\n}\n@media only screen and (min-width: 600px) {\n\t.archive-columns {\n\t\t\t/* gap: 4%;*/\n\t}\n}\n@media only screen and (min-width: 782px) {\n\t.wp-block-column.two-column {\n\t\t\tmax-width: 46%;\n\t\t\tmin-width: 46%;\n\t}\n}\n.blog .post-archive-wrap .wp-block-column h2,\n.archive h2.post--title.entry-title  {\n\tmargin-top: 35px;\n\tmargin-bottom: 35px;\n\tline-height: 37px;\n}\n.archive .entry-meta.post--byline a {\n\tcolor: #767676;\n    font-size: clamp(14px, 0.875rem + (1vw - 3.2px) * 0.217, 16px);\n}\n.error-404 label,\n.widget_top-posts .widget-inner > p {\n\tdisplay:none;\n}\n\n.archive .archive-title.page--title {\n\t    margin-bottom: 0.75rem !important;\n}\n\n.archive-description {\n\tpadding-bottom: 2.75rem;\n}\n\n.archive-description p {\n\tmargin: 0;\n}\n\n/*Hide the 'Board Member' link under each title in the https://opensource.org/blog/status/board-member page */\n.term-board-member .member-seat.inline-list{display: none;}\n\n.page-id-2549 .member-seat.inline-list{display: none;}\n\n.wp-block-osi-main-sponsors-list {\n\tborder:0 !important;\n}\n\n.alumni-board-grid {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr); /* 3 columns */\n    gap: 20px; /* Space between grid items */\n    margin-top: 20px;\n}\n\n.alumni-board-grid .wp-block-post {\n    border: 1px solid #ddd;\n    border-radius: 10px;\n    padding: 15px;\n    text-align: center;\n    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */\n}\n\n.alumni-board-grid .wp-block-post img {\n    max-width: 100%;\n    height: auto;\n    border-radius: 8px;\n    margin-bottom: 10px;\n}\n\n.updated {\n\tdisplay: none;\n}\n\n/* Single podcast template - hide date & byline */\n.single-podcast .post--byline {\n    display: none;\n}\n\n/*Single podcast template - hide series information*/\n.nelio-content-series-meta {\n    display: none;\n}\n\n.nelio-content-series-post-list__title {\n    display: none;\n}\n\n.nelio-content-series-post-list {\n    display: none;\n}\n\n#menu-item-2395 > a:first-of-type {\ncolor: var(--wp--custom--button--color--text);\n    cursor: pointer;\n    background-color: var(--wp--custom--button--color--background);\n    border: var(--wp--custom--button--border--width) var(--wp--custom--button--border--style) var(--wp--custom--button--border--color);\n    border-radius: var(--wp--custom--button--border--radius);\n    display: inline-block;\n    font-family: var(--wp--custom--button--typography--font-family);\n    -webkit-font-smoothing: auto;\n    height: auto;\n\t  font-weight: bold;\n    padding: var(--wp--custom--button--spacing--padding--top) var(--wp--custom--button--spacing--padding--right) var(--wp--custom--button--spacing--padding--bottom) var(--wp--custom--button--spacing--padding--left);\n    text-decoration: none;\n    transition: all 0.3s;\n    width: auto;\n}\n\n/* Hide Location for OSI Maintainer Month Event - @nickpagz - May 13 20205 */\n.postid-127606 section.em-event-where {\n    display:none;\n}\n\n/* Display markdown code properly - nickvidal - May 21 2025 */\ncode { font-size: 0.9em; padding: 0.15em 0.3em; border-radius: 6px; max-width: 100%; }\n\na code { color: var(--wp--preset--color--brand-links); text-decoration: underline; }\n\n\n.testimonial-rounded-corners figure img {\n  border-radius: 20px;\n}\n\n.black-white{\n\tfilter: grayscale(100%);\n}\n/* ========== Desktop ========== */\n.ai-secondary-navbar-wrapper {\n  background-color: #fff;\n  border-bottom: 1px solid #ddd;\n  padding: 10px 0;\n  position: sticky;\n  top: 90px; /* adjust as needed */\n  z-index: 999;\n}\n\n.ai-secondary-nav-menu {\n  display: flex;\n  justify-content: center;\n  gap: 40px;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n\tpadding-top:10px\n\tfont-size: 10px;\n}\n\n.ai-secondary-nav-menu li {\n  display: inline-block;\n}\n\n/* ========== Mobile fix (force override) ========== */\n@media (max-width: 768px) {\n  /* Hide desktop sticky version */\n  .ai-secondary-navbar-wrapper {\n    display: none !important;\n  }\n\n  /* Force vertical layout regardless of class */\n  .ai-secondary-nav-menu,\n  .ai-secondary-mobile-menu {\n    display: block !important;\n    flex-direction: column !important;\n    list-style: none;\n    padding: 0;\n    margin: 20px 0 0;\n  }\n\n  .ai-secondary-nav-menu li,\n  .ai-secondary-mobile-menu li {\n    display: block !important;\n    text-align: right;\n    padding: 12px 20px;\n    border-top: 1px solid rgba(255,255,255,0.1);\n  }\n\n  .ai-secondary-nav-menu a,\n  .ai-secondary-mobile-menu a {\n    color: #fff;\n    text-decoration: none;\n    font-size: 18px;\n    display: block;\n  }\n\n  .ai-secondary-nav-menu a:hover,\n  .ai-secondary-mobile-menu a:hover {\n    color: #aaa;\n  }\n}\n\n.ai-mobile-label {\n\tdisplay: none;\n}\n\n.hidden {\n\tdisplay: none !important;\n}\n@media (max-width: 768px) {\n\t.wrapper {\n\t\tpadding: 0;\n\t}\n\t\n  .ai-secondary-mobile-wrapper {\n    margin-top: 10px;\n    padding-top: 0;\n  }\n\n  .ai-mobile-label {\n    display: block;\n\t\tcolor: #bbb;\n    font-size: 13px;\n    text-transform: uppercase;\n    padding: 8px 20px 4px;\n    margin: 0;\n    letter-spacing: 0.5px;\n\t\ttext-align: right;\n  }\n\n  .ai-secondary-mobile-menu {\n    margin-top: 0;\n  }\n\n  .ai-secondary-mobile-menu li {\n    padding: 10px 20px;\n  }\n}\n\n.wrapper {\n    padding: 0 5vw 0 5vw;\n}\n\n.osaid-timeline h3 {\n\t  font-size: 12px !important;\n\t  line-height: 14px !important;\n}\n\n.osaid-previous-work h2{\n\t   font-size:26px !important;\n\t\t line-height:28px !important;\n}\n\n.content.ai-full-width h2.wp-block-heading, .osaid-faq h2 {\n\t font-size: 28px !important;\n\t line-height: 30px !important;\n}\n\n/* #menu-item-136785, #menu-item-136785 a:hover {\n\tfont-weight: bold;\n} */\t\t</style>\n\t\t</head>\n\n<body data-cmplz=1 class=\"wp-singular license-template-default single single-license postid-395 wp-custom-logo wp-embed-responsive wp-theme-osi jps-theme-osi\">\n\t<div class=\"wrapper\" role=\"document\">\n\t\t<header id=\"masthead\" class=\"site-header header-main\">\n\t\t\t<a class=\"skip-link screen-reader-text\" href=\"#content\">Skip to content</a>\n\t\t\t<div class=\"header--inner\">\n\t\t\t\t<div class=\"site-branding header--blog-name\">\n\t\t\t\t\t<a href=\"https://opensource.org\"><img class=\"header-logo\" src=\"https://i0.wp.com/opensource.org/wp-content/uploads/2023/03/cropped-OSI-horizontal-large.png?fit=640%2C229&#038;quality=80&#038;ssl=1\" alt=\"Open Source Initiative\" /></a>\t\t\t\t</div><!-- .site-branding -->\n\t\t\t\t<nav aria-label=\"Primary\" id=\"site-navigation\" class=\"nav-main\" role=\"navigation\">\n\t\t\t\t\t<div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu\" class=\"nav-main--menu\"><li id=\"menu-item-2395\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li id=\"menu-item-2358\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-2535\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-2360\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-136209\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li id=\"menu-item-5690\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-17606\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div><div class=\"menu-primary-menu-container\"><ul id=\"menu-primary-menu-1\" class=\"nav-mobile--menu\"><li class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2395\"><a href=\"/get-involved\">Get involved</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2358\"><a href=\"https://opensource.org/about\">About</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2535\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-2360\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-136209\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5690\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-17606\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n</ul></div>\t\t\t\t</nav><!-- #site-navigation -->\n\t\t\t\t<section class=\"open-search-wrapper\">\n\t\t\t\t<a aria-label=\"Open Search\" class=\"open-search open-button jetpack-search-filter__link\" href=\"#\">\n\t\t\t\t\t\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"44px\" height=\"44px\" viewBox=\"0 0 44 44\">\n<path fill=\"#414042\" d=\"M19.2568,28.5067c2.0558,0,4.0528-0.686,5.6746-1.9494l6.1188,6.1197\n\tc0.457,0.4415,1.1854,0.4288,1.6268-0.0283c0.4307-0.446,0.4307-1.153,0-1.5989l-6.1187-6.1196\n\tc3.1346-4.0357,2.4047-9.8488-1.6304-12.9839c-4.0351-3.13513-9.8473-2.40507-12.9819,1.6306\n\tc-3.13463,4.0358-2.40469,9.8488,1.6304,12.9839C15.2009,27.823,17.1997,28.5077,19.2568,28.5067z M14.3405,14.3376\n\tc2.7152-2.7158,7.1175-2.7158,9.8328-0.0001c2.7153,2.7156,2.7154,7.1186,0.0001,9.8343c-2.7152,2.7157-7.1175,2.7158-9.8328,0.0001\n\tc-0.0001,0-0.0001,0-0.0001-0.0001c-2.7153-2.6959-2.7313-7.0828-0.0358-9.7985C14.3166,14.3614,14.3285,14.3495,14.3405,14.3376z\" />\n</svg>\t\t\t\t</a>\n\t\t\t\t</section>\n\t\t\t\t<section class=\"open-button-wrapper\">\n\t\t\t\t\t\t<button aria-label=\"Open Menu\" id=\"openMainMenu\" class=\"open-main-menu open-button\">\n\t\t\t\t\t\t\t<span class=\"menu-text\">Open Main Menu</span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t\t<span></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</section>\n\t\t\t</div>\n\t\t</header><!-- #masthead -->\n\n<section class=\"content has_no_sidebar\" id=\"content\">\n\n\t<main class=\"content--body content-full\" role=\"main\">\n\t\t<section class=\"content--page\" id=\"content-page\">\n\t\t\t  <div class=\"breadcrumb-area\">\n    <div class=\"wrapper\">\n      </div></div><article id=\"post-395\" class=\"post-395 license type-license status-publish hentry taxonomy-license-category-popular-strong-community\">\n\t<header class=\"entry-header cover--header no-thumbnail\">\n\t<div class=\"wp-block-cover alignfull has-neutral-dark-background-color has-background-dim-100 has-background-dim\">\n\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 70%\">\n\t\t\t\t\t<span class=\"pill-taxonomy\">\n\t\t\t\t\t\t<div class=\"post--metadata-group\"><ul><li><a class=\"term-item\" aria-label=\"View all filed under Popular / Strong Community\" href=\"https://opensource.org/licenses/?categories=popular-strong-community\" data-id=\"19\" data-term=\"popular-strong-community\">Popular / Strong Community</a></li></ul></div>\n\t\t\t\t\t</span>\n\t\t\t\t\t<h1 class=\"entry-title page--title\">The MIT License</h1>\t\t\t\t\t<p class=\"license-meta\">\n\t\t\t\t\t\t<span class=\"license-version\">Version N/A</span>\t\t\t\t\t\t\t<span class=\"license-spdx\">\n\t\t\t\t\t\t\t\tSPDX short identifier:\n\t\t\t\t\t\t\t\tMIT\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t<div class=\"wp-block-column\" style=\"flex-basis: 30%; text-align: center;\">\n\t\t\t\t\t<img width=\"200\" src=\"/wp-content/themes/osi/assets/img/osi-badge-light.svg\" alt=\"Open Source Initiative Approved License\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</header>\n\n\t<div class=\"entry-content post--content license-content\">\n\t\t<div>\n\t\t\t<div id=\"separator\">\n<p><span style=\"font-family: var(--wp--custom--typography--body--font-family), sans-serif; font-size: revert; color: initial;\">Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt;</span></p>\n</div>\n<div id=\"LicenseText\">\n<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8220;Software&#8221;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>\n<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>\n<p>THE SOFTWARE IS PROVIDED &#8220;AS IS&#8221;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>\n</div>\n<div class=\"syndication-links\"></div>\t\t\t\n\t\t\t<hr />\n\t\t\t<div class=\"license-comments\">\n\t\t\t\t\t\t\t</div>\n\t\t</div>\n\t\t\n\t\t<aside class=\"sidebar content--sidebar license-sidebar sidebar-main\" role=\"complementary\">\n\t\t\t<section class=\"widget_text widget wp-block-column custom_html-3 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><!-- <div style=\"border: 1px solid #ccc;\n    border-radius: 15px;\n    padding: 0.5rem;\n    font-size: 10px;\">\n\t<h2 style=\"margin-top:1rem\">\n\t\tDonate to the OSI\n\t</h2>\n<p style=\"font-size:1rem\">The OSI is the authority that defines Open Source, recognized globally by individuals, companies, and public institutions.</p>\n<script async\n  src=\"https://js.stripe.com/v3/buy-button.js\">\n</script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1PtLmoCDGZvx4he3NWCcIaMW\"\n  publishable-key=\"pk_live_51NiN1oCDGZvx4he33AmqP81rHgQbxPj80MEGKvnJdxyIqVbVPsZs54yhyYk0rNTV6shPr22xlDhavp8ne3b9NpxP00MFHQ3VyC\"\n>\n</stripe-buy-button>\n\t<p style=\"font-size:0.8rem\">The Open Source Initiative (OSI) is a 501(c)3 public benefit corporation, founded in 1998.</p>\n</div> --></div></div></section>\t\t</aside><!-- .license-sidebar -->\n\t</div><!-- .entry-content -->\n\t\n\t\n\n</article><!-- #post-395 -->\n\t\t</section>\n\t</main><!-- #primary -->\n\n\t\n</section>\n\n\n\t\t<footer id=\"colophon\" class=\"footer site-footer\" role=\"contentinfo\">\n\t\t\t\t\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<section class=\"footer-cta\">\n\t\t\t\t\t\t<div class=\"wp-block-columns\">\n\t\t\t\t\t\t\t<section class=\"wp-block-column widget block-22 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"/get-involved\">Get involved</a></div>\n</div>\n</div></section><section class=\"wp-block-column widget block-16 widget_block\"><div class=\"widget-inner\"><p>\t\t\t<nav class=\"jetpack-social-navigation jetpack-social-navigation-genericons\" aria-label=\"Social Links Menu\">\n\t\t\t\t<div class=\"menu-social-menu-container\"><ul id=\"menu-social-menu\" class=\"menu\"><li id=\"menu-item-2771\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-2771\"><a href=\"https://social.opensource.org/@osi\"><span class=\"screen-reader-text\">Mastodon</span></a></li>\n<li id=\"menu-item-259\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-259\"><a href=\"https://twitter.com/OpenSourceOrg\"><span class=\"screen-reader-text\">Twitter</span></a></li>\n<li id=\"menu-item-260\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-260\"><a href=\"https://www.linkedin.com/company/open-source-initiative-osi-\"><span class=\"screen-reader-text\">LinkedIn</span></a></li>\n<li id=\"menu-item-21957\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-21957\"><a href=\"https://www.reddit.com/user/opensourceinitiative/\"><span class=\"screen-reader-text\">Reddit</span></a></li>\n</ul></div>\t\t\t</nav><!-- .jetpack-social-navigation -->\n\t\t\t</p>\n</div></section>\t\t\t\t\t\t</div>\n\t\t\t\t\t</section>\n\t\t\t\t</div>\n\t\t\t\t\t\t<section class=\"footer-main\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t<section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">About</h2><div class=\"menu-about-container\"><ul id=\"menu-about\" class=\"menu\"><li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/about\">About</a></li>\n<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/about/team\">Our team</a></li>\n<li id=\"menu-item-281587\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-281587\"><a href=\"https://opensource.org/about/board-of-directors\">Board of directors</a></li>\n<li id=\"menu-item-20858\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-20858\"><a href=\"https://opensource.org/sponsors\">Sponsors</a></li>\n<li id=\"menu-item-282530\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-282530\"><a href=\"https://opensource.org/programs\">Programs</a></li>\n<li id=\"menu-item-282531\" class=\"menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-282531\"><a href=\"https://opensource.org/blog\">Blog</a></li>\n<li id=\"menu-item-123421\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-123421\"><a href=\"https://opensource.org/press-mentions\">Press mentions</a></li>\n<li id=\"menu-item-143995\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-143995\"><a href=\"https://opensource.org/about/brand-and-trademark-guidelines\">Trademark</a></li>\n<li id=\"menu-item-8859\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8859\"><a href=\"https://opensource.org/bylaws\">Bylaws</a></li>\n</ul></div></div></div>\n</div></section><section class=\"wp-block-column widget block-18 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Licenses</h2><div class=\"menu-licenses-container\"><ul id=\"menu-licenses\" class=\"menu\"><li id=\"menu-item-5439\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5439\"><a href=\"https://opensource.org/osd\">Open Source Definition</a></li>\n<li id=\"menu-item-5440\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5440\"><a href=\"https://opensource.org/licenses\">Licenses</a></li>\n<li id=\"menu-item-5441\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5441\"><a href=\"https://opensource.org/licenses/review-process\">License Review<br />Process</a></li>\n<li id=\"menu-item-8857\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-8857\"><a href=\"https://opensource.org/osr\">Open Standards Requirement for Software</a></li>\n</ul></div></div></div>\n</div></section><section class=\"widget_text wp-block-column widget custom_html-2 widget_custom_html\"><div class=\"widget_text widget-inner\"><div class=\"textwidget custom-html-widget\"><section class=\"wp-block-column widget block-17 widget_block\"><div class=\"widget-inner\">\n\t<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n\t\t<div class=\"widget widget_nav_menu\">\n\t\t\t<h2 class=\"widgettitle\">Open Source AI</h2>\n\t\t\t<div class=\"menu-about-container\">\n\t\t\t\t<ul id=\"menu-osai\" class=\"menu\">\n\t\t\t\t\t<li id=\"menu-item-5428\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai\">Open Source AI</a></li>\n\t\t\t<li id=\"menu-item-5429\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5429\"><a href=\"https://go.opensource.org/osaid-latest\">OSAI Definition</a></li>\n\t\t\t\t\t<li id=\"menu-item-5421\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5428\"><a href=\"https://opensource.org/ai/process\">Process</a></li>\n\t\t\t<li id=\"menu-item-5430\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5430\"><a href=\"https://opensource.org/ai/timeline\">Timeline</a></li>\n\t\t\t\t\t<li id=\"menu-item-17411\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17411\"><a href=\"https://opensource.org/ai/open-weights\">Open Weights</a></li>\n\t\t\t\t\t<li id=\"menu-item-24456\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24456\"><a href=\"https://opensource.org/ai/faq\">FAQ</a></li>\n\t\t\t\t\t<li id=\"menu-item-24457\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-24457\"><a href=\"https://opensource.org/ai/checklist\">Checklist</a></li>\n\t\t\t\t\t<li id=\"menu-item-17410\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-17410\"><a href=\"https://opensource.org/ai/forums\">Forum</a></li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n</section></div></div></section><section class=\"wp-block-column widget block-21 widget_block\"><div class=\"widget-inner\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\"><div class=\"widget widget_nav_menu\"><h2 class=\"widgettitle\">Community</h2><div class=\"menu-community-container\"><ul id=\"menu-community\" class=\"menu\"><li id=\"menu-item-5485\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5485\"><a href=\"https://opensource.org/members\">Become an Individual Member</a></li>\n<li id=\"menu-item-5438\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5438\"><a href=\"https://opensource.org/affiliates/about\">Become an OSI Affiliate</a></li>\n<li id=\"menu-item-5482\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-5482\"><a href=\"https://opensource.org/affiliates\">Affiliate Organizations</a></li>\n<li id=\"menu-item-136230\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-136230\"><a href=\"https://opensource.org/maintainers\">Maintainers</a></li>\n<li id=\"menu-item-7949\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-7949\"><a href=\"https://opensource.org/events/\">Events</a></li>\n<li id=\"menu-item-122599\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122599\"><a href=\"https://discuss.opensource.org\">Forum</a></li>\n<li id=\"menu-item-122600\" class=\"menu-item menu-item-type-custom menu-item-object-custom menu-item-122600\"><a href=\"https://opensource.net\">OpenSource.net</a></li>\n</ul></div></div></div>\n</div></section>\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t<section class=\"footer-credits\">\n\t\t\t\t<div class=\"footer--inner\">\n\t\t\t\t\t<div class=\"widgetized-footer footer--widgets wp-block-columns alignwide\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"wp-block-column\">\n\t\t\t\t\t\t\t<p class=\"footer--extra-text\">\n\t\t\t\t\t\t\t<div id=\"block-23\" class=\"widget widget_block widget_text\">\n<p>The content on this website, of which Opensource.org is the author, is licensed under a <a href=\"https://web.archive.org/web/20230202005829/https://creativecommons.org/licenses/by/4.0/\">Creative Commons Attribution 4.0 International License</a>.<br><br>Opensource.org is not the author of any of the licenses reproduced on this site. Questions about the copyright in a license should be directed to the license steward. Read our <a href=\"https://opensource.org/privacy\">Privacy Policy</a></p>\n</div>\t\t\t\t\t\t\t\t<a href=\"https://wordpress.com/wp/?partner_domain=opensource.org&#038;utm_source=Automattic&#038;utm_medium=colophon&#038;utm_campaign=Concierge%20Referral&#038;utm_term=opensource.org\" class=\"imprint\" target=\"_blank\">Proudly powered by WordPress.</a> <a href=\"https://pressable.com/?utm_source=Automattic&#038;utm_medium=rpc&#038;utm_campaign=Concierge%20Referral&#038;utm_term=concierge\" class=\"imprint\" target=\"_blank\">Hosted by Pressable.</a>\t\t\t\t\t\t\t</p><!-- .powered-by-wordpress -->\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</footer><!-- #colophon -->\n\n\t</div><!-- #page -->\n\n<script type=\"speculationrules\">\n{\"prefetch\":[{\"source\":\"document\",\"where\":{\"and\":[{\"href_matches\":\"/*\"},{\"not\":{\"href_matches\":[\"/wp-*.php\",\"/wp-admin/*\",\"/wp-content/uploads/*\",\"/wp-content/*\",\"/wp-content/plugins/*\",\"/wp-content/themes/osi/*\",\"/*\\\\?(.+)\"]}},{\"not\":{\"selector_matches\":\"a[rel~=\\\"nofollow\\\"]\"}},{\"not\":{\"selector_matches\":\".no-prefetch, .no-prefetch a\"}}]},\"eagerness\":\"conservative\"}]}\n</script>\n\t\t<script type=\"text/javascript\">\n\t\t\t(function() {\n\t\t\t\tlet targetObjectName = 'EM';\n\t\t\t\tif ( typeof window[targetObjectName] === 'object' && window[targetObjectName] !== null ) {\n\t\t\t\t\tObject.assign( window[targetObjectName], []);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn( 'Could not merge extra data: window.' + targetObjectName + ' not found or not an object.' );\n\t\t\t\t}\n\t\t\t})();\n\t\t</script>\n\t\t\t\t<div class=\"jetpack-instant-search__widget-area\" style=\"display: none\">\n\t\t\t\t\t\t\t<div id=\"jetpack-search-filters-4\" class=\"widget jetpack-filters widget_search\">\t\t\t<div id=\"jetpack-search-filters-4-wrapper\" class=\"jetpack-instant-search-wrapper\">\n\t\t</div></div>\t\t\t\t\t</div>\n\t\t\n<!-- Consent Management powered by Complianz | GDPR/CCPA Cookie Consent https://wordpress.org/plugins/complianz-gdpr -->\n<div id=\"cmplz-cookiebanner-container\"><div class=\"cmplz-cookiebanner cmplz-hidden banner-1 bottom-right-view-preferences optin cmplz-bottom-right cmplz-categories-type-view-preferences\" aria-modal=\"true\" data-nosnippet=\"true\" role=\"dialog\" aria-live=\"polite\" aria-labelledby=\"cmplz-header-1-optin\" aria-describedby=\"cmplz-message-1-optin\">\n\t<div class=\"cmplz-header\">\n\t\t<div class=\"cmplz-logo\"></div>\n\t\t<div class=\"cmplz-title\" id=\"cmplz-header-1-optin\">Manage Cookie Consent</div>\n\t\t<div class=\"cmplz-close\" tabindex=\"0\" role=\"button\" aria-label=\"Close dialog\">\n\t\t\t<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"times\" class=\"svg-inline--fa fa-times fa-w-11\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"currentColor\" d=\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\"></path></svg>\n\t\t</div>\n\t</div>\n\n\t<div class=\"cmplz-divider cmplz-divider-header\"></div>\n\t<div class=\"cmplz-body\">\n\t\t<div class=\"cmplz-message\" id=\"cmplz-message-1-optin\">To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.</div>\n\t\t<!-- categories start -->\n\t\t<div class=\"cmplz-categories\">\n\t\t\t<details class=\"cmplz-category cmplz-functional\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Functional</span>\n\t\t\t\t\t\t\t<span class='cmplz-always-active'>\n\t\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t   id=\"cmplz-functional-optin\"\n\t\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_functional\"\n\t\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-functional\"\n\t\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-functional-optin\"><span class=\"screen-reader-text\">Functional</span></label>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\tAlways active\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-functional\">The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-preferences\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Preferences</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-preferences-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_preferences\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-preferences\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-preferences-optin\"><span class=\"screen-reader-text\">Preferences</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-preferences\">The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\n\t\t\t<details class=\"cmplz-category cmplz-statistics\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Statistics</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-statistics-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_statistics\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-statistics\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-statistics-optin\"><span class=\"screen-reader-text\">Statistics</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-statistics\">The technical storage or access that is used exclusively for statistical purposes.</span>\n\t\t\t\t\t<span class=\"cmplz-description-statistics-anonymous\">The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t\t<details class=\"cmplz-category cmplz-marketing\" >\n\t\t\t\t<summary>\n\t\t\t\t\t\t<span class=\"cmplz-category-header\">\n\t\t\t\t\t\t\t<span class=\"cmplz-category-title\">Marketing</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-banner-checkbox\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\"\n\t\t\t\t\t\t\t\t\t   id=\"cmplz-marketing-optin\"\n\t\t\t\t\t\t\t\t\t   data-category=\"cmplz_marketing\"\n\t\t\t\t\t\t\t\t\t   class=\"cmplz-consent-checkbox cmplz-marketing\"\n\t\t\t\t\t\t\t\t\t   size=\"40\"\n\t\t\t\t\t\t\t\t\t   value=\"1\"/>\n\t\t\t\t\t\t\t\t<label class=\"cmplz-label\" for=\"cmplz-marketing-optin\"><span class=\"screen-reader-text\">Marketing</span></label>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cmplz-icon cmplz-open\">\n\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"  height=\"18\" ><path d=\"M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z\"/></svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t</summary>\n\t\t\t\t<div class=\"cmplz-description\">\n\t\t\t\t\t<span class=\"cmplz-description-marketing\">The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.</span>\n\t\t\t\t</div>\n\t\t\t</details>\n\t\t</div><!-- categories end -->\n\t\t\t</div>\n\n\t<div class=\"cmplz-links cmplz-information\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-options cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">Manage options</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-third-parties cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-cookies-overview\">Manage services</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-manage-vendors tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-tcf-wrapper\">Manage {vendor_count} vendors</a></li>\n\t\t\t<li><a class=\"cmplz-link cmplz-external cmplz-read-more-purposes tcf\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://cookiedatabase.org/tcf/purposes/\" aria-label=\"Read more about TCF purposes on Cookie Database\">Read more about these purposes</a></li>\n\t\t</ul>\n\t\t\t</div>\n\n\t<div class=\"cmplz-divider cmplz-footer\"></div>\n\n\t<div class=\"cmplz-buttons\">\n\t\t<button class=\"cmplz-btn cmplz-accept\">Accept</button>\n\t\t<button class=\"cmplz-btn cmplz-deny\">Deny</button>\n\t\t<button class=\"cmplz-btn cmplz-view-preferences\">View preferences</button>\n\t\t<button class=\"cmplz-btn cmplz-save-preferences\">Save preferences</button>\n\t\t<a class=\"cmplz-btn cmplz-manage-options tcf cookie-statement\" href=\"#\" data-relative_url=\"#cmplz-manage-consent-container\">View preferences</a>\n\t\t\t</div>\n\n\t\n\t<div class=\"cmplz-documents cmplz-links\">\n\t\t<ul>\n\t\t\t<li><a class=\"cmplz-link cookie-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link privacy-statement\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t\t<li><a class=\"cmplz-link impressum\" href=\"#\" data-relative_url=\"\">{title}</a></li>\n\t\t</ul>\n\t\t\t</div>\n</div>\n</div>\n\t\t\t\t\t<div id=\"cmplz-manage-consent\" data-nosnippet=\"true\"><button class=\"cmplz-btn cmplz-hidden cmplz-manage-consent manage-consent-1\">Manage consent</button>\n\n</div><script type=\"text/javascript\" src=\"https://opensource.org/wp-content/mu-plugins/osi-editor-tweaks/build/scripts/theme/theme.js?ver=eb7b34d85380496c7763\" id=\"osi-et-theme-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/coblocks/dist/js/coblocks-animation.js?ver=3.1.16\" id=\"coblocks-animation-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/hooks/index.min.js?ver=1764773745495\" id=\"wp-hooks-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/i18n/index.min.js?ver=1764773747362\" id=\"wp-i18n-js\"></script>\n<script type=\"text/javascript\" id=\"wp-i18n-js-after\">\n/* <![CDATA[ */\nwp.i18n.setLocaleData( { 'text direction\\u0004ltr': [ 'ltr' ] } );\n//# sourceURL=wp-i18n-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4\" id=\"swv-js\"></script>\n<script type=\"text/javascript\" id=\"contact-form-7-js-before\">\n/* <![CDATA[ */\nvar wpcf7 = {\n    \"api\": {\n        \"root\": \"https:\\/\\/opensource.org\\/wp-json\\/\",\n        \"namespace\": \"contact-form-7\\/v1\"\n    },\n    \"cached\": 1\n};\n//# sourceURL=contact-form-7-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4\" id=\"contact-form-7-js\"></script>\n<script type=\"text/javascript\" id=\"load_comments_js-js-extra\">\n/* <![CDATA[ */\nvar wpdc = {\"commentsURL\":\"https://opensource.org/wp-json/wp-discourse/v1/discourse-comments\"};\n//# sourceURL=load_comments_js-js-extra\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/wp-discourse/lib/../js/load-comments.js?ver=1761590360\" id=\"load_comments_js-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/build/i18n-loader.js?minify=true&amp;ver=517685b2423141b3a0a3\" id=\"wp-jp-i18n-loader-js\"></script>\n<script type=\"text/javascript\" id=\"wp-jp-i18n-loader-js-after\">\n/* <![CDATA[ */\nwp.jpI18nLoader.state = {\"baseUrl\":\"https://opensource.org/wp-content/languages/\",\"locale\":\"en_US\",\"domainMap\":{\"jetpack-admin-ui\":\"plugins/jetpack-boost\",\"jetpack-assets\":\"plugins/jetpack\",\"jetpack-boost-core\":\"plugins/jetpack-boost\",\"jetpack-boost-speed-score\":\"plugins/jetpack-boost\",\"jetpack-config\":\"plugins/jetpack-boost\",\"jetpack-connection\":\"plugins/jetpack\",\"jetpack-explat\":\"plugins/jetpack\",\"jetpack-image-cdn\":\"plugins/jetpack-boost\",\"jetpack-ip\":\"plugins/jetpack-boost\",\"jetpack-jitm\":\"plugins/jetpack\",\"jetpack-licensing\":\"plugins/jetpack-boost\",\"jetpack-my-jetpack\":\"plugins/jetpack\",\"jetpack-password-checker\":\"plugins/jetpack-boost\",\"jetpack-plugin-deactivation\":\"plugins/jetpack-boost\",\"jetpack-plugins-installer\":\"plugins/jetpack-boost\",\"jetpack-protect-models\":\"plugins/jetpack-boost\",\"jetpack-protect-status\":\"plugins/jetpack\",\"jetpack-schema\":\"plugins/jetpack-boost\",\"jetpack-sync\":\"plugins/jetpack\",\"jetpack-wp-js-data-sync\":\"plugins/jetpack-boost\",\"jetpack-account-protection\":\"plugins/jetpack\",\"jetpack-backup-pkg\":\"plugins/jetpack\",\"jetpack-blaze\":\"plugins/jetpack\",\"jetpack-block-delimiter\":\"plugins/jetpack\",\"jetpack-classic-theme-helper\":\"plugins/jetpack\",\"jetpack-compat\":\"plugins/jetpack\",\"jetpack-external-connections\":\"plugins/jetpack\",\"jetpack-external-media\":\"plugins/jetpack\",\"jetpack-forms\":\"plugins/jetpack\",\"jetpack-import\":\"plugins/jetpack\",\"jetpack-jwt\":\"plugins/jetpack\",\"jetpack-masterbar\":\"plugins/jetpack\",\"jetpack-newsletter\":\"plugins/jetpack\",\"jetpack-paypal-payments\":\"plugins/jetpack\",\"jetpack-post-list\":\"plugins/jetpack\",\"jetpack-publicize-pkg\":\"plugins/jetpack\",\"jetpack-search-pkg\":\"plugins/jetpack\",\"jetpack-stats\":\"plugins/jetpack\",\"jetpack-stats-admin\":\"plugins/jetpack\",\"jetpack-subscribers-dashboard\":\"plugins/jetpack\",\"jetpack-videopress-pkg\":\"plugins/jetpack\",\"jetpack-waf\":\"plugins/jetpack\",\"woocommerce-analytics\":\"plugins/jetpack\"},\"domainPaths\":{\"jetpack-admin-ui\":\"jetpack_vendor/automattic/jetpack-admin-ui/\",\"jetpack-assets\":\"jetpack_vendor/automattic/jetpack-assets/\",\"jetpack-boost-core\":\"jetpack_vendor/automattic/jetpack-boost-core/\",\"jetpack-boost-speed-score\":\"jetpack_vendor/automattic/jetpack-boost-speed-score/\",\"jetpack-config\":\"jetpack_vendor/automattic/jetpack-config/\",\"jetpack-connection\":\"jetpack_vendor/automattic/jetpack-connection/\",\"jetpack-explat\":\"jetpack_vendor/automattic/jetpack-explat/\",\"jetpack-image-cdn\":\"jetpack_vendor/automattic/jetpack-image-cdn/\",\"jetpack-ip\":\"jetpack_vendor/automattic/jetpack-ip/\",\"jetpack-jitm\":\"jetpack_vendor/automattic/jetpack-jitm/\",\"jetpack-licensing\":\"jetpack_vendor/automattic/jetpack-licensing/\",\"jetpack-my-jetpack\":\"jetpack_vendor/automattic/jetpack-my-jetpack/\",\"jetpack-password-checker\":\"jetpack_vendor/automattic/jetpack-password-checker/\",\"jetpack-plugin-deactivation\":\"jetpack_vendor/automattic/jetpack-plugin-deactivation/\",\"jetpack-plugins-installer\":\"jetpack_vendor/automattic/jetpack-plugins-installer/\",\"jetpack-protect-models\":\"jetpack_vendor/automattic/jetpack-protect-models/\",\"jetpack-protect-status\":\"jetpack_vendor/automattic/jetpack-protect-status/\",\"jetpack-schema\":\"jetpack_vendor/automattic/jetpack-schema/\",\"jetpack-sync\":\"jetpack_vendor/automattic/jetpack-sync/\",\"jetpack-wp-js-data-sync\":\"jetpack_vendor/automattic/jetpack-wp-js-data-sync/\",\"jetpack-account-protection\":\"jetpack_vendor/automattic/jetpack-account-protection/\",\"jetpack-backup-pkg\":\"jetpack_vendor/automattic/jetpack-backup/\",\"jetpack-blaze\":\"jetpack_vendor/automattic/jetpack-blaze/\",\"jetpack-block-delimiter\":\"jetpack_vendor/automattic/block-delimiter/\",\"jetpack-classic-theme-helper\":\"jetpack_vendor/automattic/jetpack-classic-theme-helper/\",\"jetpack-compat\":\"jetpack_vendor/automattic/jetpack-compat/\",\"jetpack-external-connections\":\"jetpack_vendor/automattic/jetpack-external-connections/\",\"jetpack-external-media\":\"jetpack_vendor/automattic/jetpack-external-media/\",\"jetpack-forms\":\"jetpack_vendor/automattic/jetpack-forms/\",\"jetpack-import\":\"jetpack_vendor/automattic/jetpack-import/\",\"jetpack-jwt\":\"jetpack_vendor/automattic/jetpack-jwt/\",\"jetpack-masterbar\":\"jetpack_vendor/automattic/jetpack-masterbar/\",\"jetpack-newsletter\":\"jetpack_vendor/automattic/jetpack-newsletter/\",\"jetpack-paypal-payments\":\"jetpack_vendor/automattic/jetpack-paypal-payments/\",\"jetpack-post-list\":\"jetpack_vendor/automattic/jetpack-post-list/\",\"jetpack-publicize-pkg\":\"jetpack_vendor/automattic/jetpack-publicize/\",\"jetpack-search-pkg\":\"jetpack_vendor/automattic/jetpack-search/\",\"jetpack-stats\":\"jetpack_vendor/automattic/jetpack-stats/\",\"jetpack-stats-admin\":\"jetpack_vendor/automattic/jetpack-stats-admin/\",\"jetpack-subscribers-dashboard\":\"jetpack_vendor/automattic/jetpack-subscribers-dashboard/\",\"jetpack-videopress-pkg\":\"jetpack_vendor/automattic/jetpack-videopress/\",\"jetpack-waf\":\"jetpack_vendor/automattic/jetpack-waf/\",\"woocommerce-analytics\":\"jetpack_vendor/automattic/woocommerce-analytics/\"}};\n//# sourceURL=wp-jp-i18n-loader-js-after\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://c0.wp.com/c/6.9/wp-includes/js/dist/vendor/wp-polyfill.min.js\" id=\"wp-polyfill-js\"></script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/gutenberg/build/scripts/url/index.min.js?ver=1764773745605\" id=\"wp-url-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-instant-search-js-before\">\n/* <![CDATA[ */\nvar JetpackInstantSearchOptions=JSON.parse(decodeURIComponent(\"%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23fdd7b9%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%22attachment%22%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A12%2C%22siteId%22%3A210318891%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%2C%22supporter%22%3A%7B%22singular_name%22%3A%22Supporter%22%2C%22name%22%3A%22Supporters%22%7D%2C%22board-member%22%3A%7B%22singular_name%22%3A%22Board%20Member%22%2C%22name%22%3A%22Board%20Members%22%7D%2C%22license%22%3A%7B%22singular_name%22%3A%22License%22%2C%22name%22%3A%22Licenses%22%7D%2C%22meeting-minutes%22%3A%7B%22singular_name%22%3A%22Meeting%20Minutes%22%2C%22name%22%3A%22Meeting%20Minutes%22%7D%2C%22press-mentions%22%3A%7B%22singular_name%22%3A%22Press%20mentions%22%2C%22name%22%3A%22Press%20mentions%22%7D%2C%22podcast%22%3A%7B%22singular_name%22%3A%22Episode%22%2C%22name%22%3A%22Episode%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Atrue%2C%22isFreePlan%22%3Afalse%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fopensource.org%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%227d68a4059a%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Atrue%2C%22widgets%22%3A%5B%7B%22filters%22%3A%5B%7B%22name%22%3A%22Post%20Types%22%2C%22type%22%3A%22post_type%22%2C%22count%22%3A6%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22post_type_0%22%7D%2C%7B%22name%22%3A%22Categories%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22category%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_1%22%7D%2C%7B%22name%22%3A%22Tags%22%2C%22type%22%3A%22taxonomy%22%2C%22taxonomy%22%3A%22post_tag%22%2C%22count%22%3A5%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22taxonomy_2%22%7D%2C%7B%22name%22%3A%22Year%22%2C%22type%22%3A%22date_histogram%22%2C%22count%22%3A5%2C%22field%22%3A%22post_date%22%2C%22interval%22%3A%22year%22%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%2C%22filter_id%22%3A%22date_histogram_3%22%7D%5D%2C%22widget_id%22%3A%22jetpack-search-filters-4%22%7D%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D\"));\n//# sourceURL=jetpack-instant-search-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/build/instant-search/jp-search.js?minify=false&amp;ver=a9cf87fff4e38de9bc98\" id=\"jetpack-instant-search-js\"></script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"//stats.wp.com/w.js?ver=202549\" id=\"jp-tracks-js\"></script>\n<script type=\"text/javascript\" id=\"osi-theme-scripts-js-before\">\n/* <![CDATA[ */\nconst OSI_LICENSE_DB = {\"ajaxURL\":\"https:\\/\\/opensource.org\\/wp-admin\\/admin-ajax.php\",\"action\":\"osi_license_db\"}\n//# sourceURL=osi-theme-scripts-js-before\n/* ]]> */\n</script>\n<script type=\"text/javascript\" src=\"https://opensource.org/wp-content/themes/osi/assets/js/build/theme.js?ver=1724798073\" id=\"osi-theme-scripts-js\"></script>\n<script type=\"text/javascript\" src=\"https://unpkg.com/swiper@11/swiper-bundle.min.js?ver=1750065233\" id=\"swiper-js-js\"></script>\n<script type=\"text/javascript\" id=\"jetpack-stats-js-before\">\n/* <![CDATA[ */\n_stq = window._stq || [];\n_stq.push([ \"view\", JSON.parse(\"{\\\"v\\\":\\\"ext\\\",\\\"blog\\\":\\\"210318891\\\",\\\"post\\\":\\\"395\\\",\\\"tz\\\":\\\"0\\\",\\\"srv\\\":\\\"opensource.org\\\",\\\"hp\\\":\\\"atomic\\\",\\\"ac\\\":\\\"3\\\",\\\"amp\\\":\\\"0\\\",\\\"j\\\":\\\"1:15.3.1\\\"}\") ]);\n_stq.push([ \"clickTrackerInit\", \"210318891\", \"395\" ]);\n//# sourceURL=jetpack-stats-js-before\n/* ]]> */\n</script>\n<script data-service=\"jetpack-statistics\" data-category=\"statistics\" type=\"text/plain\" data-cmplz-src=\"https://stats.wp.com/e-202549.js\" id=\"jetpack-stats-js\" defer=\"defer\" data-wp-strategy=\"defer\"></script>\n<script type=\"text/javascript\" id=\"cmplz-cookiebanner-js-extra\">\n/* <![CDATA[ */\nvar complianz = {\"prefix\":\"cmplz_\",\"user_banner_id\":\"1\",\"set_cookies\":[],\"block_ajax_content\":\"\",\"banner_version\":\"124\",\"version\":\"7.4.4.1\",\"store_consent\":\"\",\"do_not_track_enabled\":\"1\",\"consenttype\":\"optin\",\"region\":\"eu\",\"geoip\":\"\",\"dismiss_timeout\":\"\",\"disable_cookiebanner\":\"1\",\"soft_cookiewall\":\"\",\"dismiss_on_scroll\":\"\",\"cookie_expiry\":\"365\",\"url\":\"https://opensource.org/wp-json/complianz/v1/\",\"locale\":\"lang=en&locale=en_US\",\"set_cookies_on_root\":\"\",\"cookie_domain\":\"\",\"current_policy_id\":\"22\",\"cookie_path\":\"/\",\"categories\":{\"statistics\":\"statistics\",\"marketing\":\"marketing\"},\"tcf_active\":\"\",\"placeholdertext\":\"Click to accept {category} cookies and enable this content\",\"css_file\":\"https://opensource.org/wp-content/uploads/complianz/css/banner-{banner_id}-{type}.css?v=124\",\"page_links\":{\"eu\":{\"cookie-statement\":{\"title\":\"\",\"url\":\"https://opensource.org/blog/dpgas-annual-members-meeting-advancing-open-source-dpgs-for-the-public-good\"}}},\"tm_categories\":\"\",\"forceEnableStats\":\"\",\"preview\":\"\",\"clean_cookies\":\"\",\"aria_label\":\"Click to accept {category} cookies and enable this content\"};\n//# sourceURL=cmplz-cookiebanner-js-extra\n/* ]]> */\n</script>\n<script defer type=\"text/javascript\" src=\"https://opensource.org/wp-content/plugins/complianz-gdpr/cookiebanner/js/complianz.min.js?ver=1764787187\" id=\"cmplz-cookiebanner-js\"></script>\n<script id=\"wp-emoji-settings\" type=\"application/json\">\n{\"baseUrl\":\"https://s.w.org/images/core/emoji/17.0.2/72x72/\",\"ext\":\".png\",\"svgUrl\":\"https://s.w.org/images/core/emoji/17.0.2/svg/\",\"svgExt\":\".svg\",\"source\":{\"concatemoji\":\"https://opensource.org/wp-includes/js/wp-emoji-release.min.js?ver=6.9\"}}\n</script>\n<script type=\"module\">\n/* <![CDATA[ */\n/*! This file is auto-generated */\nconst a=JSON.parse(document.getElementById(\"wp-emoji-settings\").textContent),o=(window._wpemojiSettings=a,\"wpEmojiSettingsSupports\"),s=[\"flag\",\"emoji\"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,a){switch(t){case\"flag\":return n(e,\"\\ud83c\\udff3\\ufe0f\\u200d\\u26a7\\ufe0f\",\"\\ud83c\\udff3\\ufe0f\\u200b\\u26a7\\ufe0f\")?!1:!n(e,\"\\ud83c\\udde8\\ud83c\\uddf6\",\"\\ud83c\\udde8\\u200b\\ud83c\\uddf6\")&&!n(e,\"\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40\\udc65\\udb40\\udc6e\\udb40\\udc67\\udb40\\udc7f\",\"\\ud83c\\udff4\\u200b\\udb40\\udc67\\u200b\\udb40\\udc62\\u200b\\udb40\\udc65\\u200b\\udb40\\udc6e\\u200b\\udb40\\udc67\\u200b\\udb40\\udc7f\");case\"emoji\":return!a(e,\"\\ud83e\\u1fac8\")}return!1}function f(e,t,n,a){let r;const o=(r=\"undefined\"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement(\"canvas\")).getContext(\"2d\",{willReadFrequently:!0}),s=(o.textBaseline=\"top\",o.font=\"600 32px Arial\",{});return e.forEach(e=>{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement(\"script\");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if(\"object\"==typeof e&&\"number\"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&\"object\"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if(\"undefined\"!=typeof Worker&&\"undefined\"!=typeof OffscreenCanvas&&\"undefined\"!=typeof URL&&URL.createObjectURL&&\"undefined\"!=typeof Blob)try{var e=\"postMessage(\"+f.toString()+\"(\"+[JSON.stringify(s),u.toString(),c.toString(),p.toString()].join(\",\")+\"));\",a=new Blob([e],{type:\"text/javascript\"});const r=new Worker(URL.createObjectURL(a),{name:\"wpTestEmojiSupports\"});return void(r.onmessage=e=>{i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],\"flag\"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))});\n//# sourceURL=https://opensource.org/wp-includes/js/wp-emoji-loader.min.js\n/* ]]> */\n</script>\n\n<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9a9e16ebac7eda97',t:'MTc2NTA0ODI5MQ=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>\n</html>\n<!--\n\tgenerated 197 seconds ago\n\tgenerated in 0.721 seconds\n\tserved from batcache in 0.003 seconds\n\texpires in 103 seconds\n-->\n"
  },
  {
    "path": "BORGCalendar/install/src/main/resources/run_borg.sh",
    "content": "cd \"%{INSTALL_PATH}\"\njre/bin/java -Xmx128m -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -Xms24m -jar borg.jar"
  },
  {
    "path": "BORGCalendar/install/winpackage.bat",
    "content": "C:\\Users\\deskp\\Documents\\jdk-21.0.2\\bin\\jpackage -t msi --app-version 2.0.0 -n BorgCalendar --vendor MBCSoft -i target\\installer --main-class net.sf.borg.control.Borg --main-jar borg.jar --win-dir-chooser --win-menu --win-menu-group Borg --win-shortcut --icon borg.ico\n"
  },
  {
    "path": "BORGCalendar/model/.gitignore",
    "content": "/.classpath\n"
  },
  {
    "path": "BORGCalendar/model/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>BORGCalendar</groupId>\n\t\t<artifactId>BORGCalendar</artifactId>\n\t\t<version>2.0</version>\n\t</parent>\n\t<artifactId>model</artifactId>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>BORGCalendar</groupId>\n\t\t\t<artifactId>common</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.h2database</groupId>\n\t\t\t<artifactId>h2</artifactId>\n\t\t\t<version>2.4.240</version>\n\t\t\t<scope>runtime</scope>\n\t\t</dependency>\n\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.mnode.ical4j</groupId>\n\t\t\t<artifactId>ical4j</artifactId>\n\t\t\t<version>2.2.5</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.mnode.ical4j</groupId>\n\t\t\t<artifactId>ical4j-vcard</artifactId>\n\t\t\t<version>1.0.1</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.codehaus.groovy</groupId>\n\t\t\t\t\t<artifactId>groovy-all</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>jakarta.xml.bind</groupId>\n\t\t\t<artifactId>jakarta.xml.bind-api</artifactId>\n\t\t\t<version>4.0.4</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.sun.xml.bind</groupId>\n\t\t\t<artifactId>jaxb-core</artifactId>\n\t\t\t<version>4.0.6</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.sun.xml.bind</groupId>\n\t\t\t<artifactId>jaxb-impl</artifactId>\n\t\t\t<version>4.0.6</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.api-client</groupId>\n\t\t\t<artifactId>google-api-client</artifactId>\n\t\t\t<version>2.8.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.oauth-client</groupId>\n\t\t\t<artifactId>google-oauth-client-jetty</artifactId>\n\t\t\t<version>1.39.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.apis</groupId>\n\t\t\t<artifactId>google-api-services-calendar</artifactId>\n\t\t\t<version>v3-rev20251207-2.0.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.apis</groupId>\n\t\t\t<artifactId>google-api-services-tasks</artifactId>\n\t\t\t<version>v1-rev20251102-2.0.0</version>\n\t\t</dependency>\n    <dependency>\n      <groupId>com.google.apis</groupId>\n      <artifactId>google-api-services-drive</artifactId>\n    <version>v3-rev20251210-2.0.0</version>\n    </dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.xerial</groupId>\n\t\t\t<artifactId>sqlite-jdbc</artifactId>\n\t\t\t<version>3.51.1.0</version>\n\t\t</dependency>\n\n\t</dependencies>\n</project>"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/AddressModel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.model;\n\nimport jakarta.xml.bind.JAXBContext;\nimport jakarta.xml.bind.Marshaller;\nimport jakarta.xml.bind.Unmarshaller;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.EntityDB;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.LabelEntity;\nimport net.sf.borg.model.entity.Link;\nimport net.sf.borg.model.undo.AddressUndoItem;\nimport net.sf.borg.model.undo.UndoLog;\n\nimport java.io.InputStream;\nimport java.io.Writer;\nimport java.util.*;\n\n/**\n * AddressModel provides the model layer APIs for working with Addresses\n */\npublic class AddressModel extends Model implements Searchable<Address>,\n        CalendarEntityProvider {\n\n    static private final AddressModel self_ = new AddressModel();\n\n    /**\n     * class XmlContainer is solely for JAXB XML export/import to keep the same\n     * XML structure as before JAXB was used\n     */\n    @XmlRootElement(name = \"ADDRESSES\")\n    private static class XmlContainer {\n        public Collection<Address> Address;\n    }\n\n    /**\n     * Gets the reference.\n     *\n     * @return the reference\n     */\n    public static AddressModel getReference() {\n        return (self_);\n    }\n\n    /**\n     * compute an integer has key from a date\n     *\n     * @param d the date\n     * @return the integer key\n     */\n    private static int birthdayKey(Date d) {\n        GregorianCalendar g = new GregorianCalendar();\n        g.setTime(d);\n        return g.get(Calendar.MONTH) * 100 + g.get(Calendar.DATE);\n    }\n\n    /**\n     * map of birthdays to addresses\n     */\n    private final HashMap<Integer, LinkedList<Address>> bdmap_ = new HashMap<Integer, LinkedList<Address>>();\n\n    /**\n     * The db_.\n     */\n    private final EntityDB<Address> db_; // the database\n\n    /**\n     * Instantiates a new address model.\n     */\n    private AddressModel() {\n        db_ = DBHelper.getFactory().createAddressDB();\n        load_map();\n    }\n\n    /**\n     * Delete an Address\n     *\n     * @param addr the Address\n     */\n    public void delete(Address addr) {\n        delete(addr, false);\n    }\n\n    /**\n     * Delete an Address.\n     *\n     * @param addr the Address\n     * @param undo true if we are executing an undo\n     */\n    public void delete(Address addr, boolean undo) {\n\n        try {\n            Address orig_addr = getAddress(addr.getKey());\n            LinkModel.getReference().deleteLinksFromEntity(addr);\n            LinkModel.getReference().deleteLinksToEntity(addr);\n\n            db_.delete(addr.getKey());\n\n            // don't store an undo record if we are currently running an undo\n            if (!undo) {\n                UndoLog.getReference().addItem(\n                        AddressUndoItem.recordDelete(orig_addr));\n            }\n\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n\n        refresh();\n    }\n\n    /**\n     * Export all Addresses to XML.\n     *\n     * @param fw the Writer to write XML to\n     * @throws Exception the exception\n     */\n    @Override\n    public void export(Writer fw) throws Exception {\n\n        JAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\n        Marshaller m = jc.createMarshaller();\n        XmlContainer container = new XmlContainer();\n        container.Address = getAddresses();\n        m.marshal(container, fw);\n\n    }\n\n    /**\n     * Get an Address by key\n     *\n     * @param num the key\n     * @return the address\n     * @throws Exception the exception\n     */\n    public Address getAddress(int num) throws Exception {\n        return (db_.readObj(num));\n    }\n\n    /**\n     * Get all addresses.\n     *\n     * @return the addresses\n     * @throws Exception the exception\n     */\n    public Collection<Address> getAddresses() throws Exception {\n        Collection<Address> addrs = db_.readAll();\n        return addrs;\n    }\n\n    /**\n     * Get all addresses with birthdays on a given day.\n     *\n     * @param d the day\n     * @return the addresses\n     */\n    public Collection<Address> getAddresses(Date d) {\n        // don't consider year for birthdays\n        int bdkey = birthdayKey(d);\n        return (bdmap_.get(Integer.valueOf(bdkey)));\n    }\n\n    /**\n     * Gets the dB.\n     *\n     * @return the dB\n     */\n    @Deprecated\n    public EntityDB<Address> getDB() {\n        return (db_);\n    }\n\n    /**\n     * Import xml.\n     *\n     * @param is the input stream containing the XML\n     * @throws Exception the exception\n     */\n    @Override\n    public void importXml(InputStream is) throws Exception {\n\n        JAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\n        Unmarshaller u = jc.createUnmarshaller();\n\n        XmlContainer container = (XmlContainer) u.unmarshal(is);\n\n        if (container.Address == null)\n            return;\n\n        // use key from import file if importing into empty db\n        int nextkey = db_.nextkey();\n        boolean use_keys = nextkey == 1;\n        for (Address addr : container.Address) {\n            if (!use_keys)\n                addr.setKey(nextkey++);\n\n            try {\n                db_.addObj(addr);\n            } catch (Warning e) {\n                Errmsg.getErrorHandler().notice(e.getMessage() + \"\\n\\n\" + addr.toString());\n            }\n        }\n\n        refresh();\n    }\n\n    /**\n     * populate the map of birthdays\n     */\n    private void load_map() {\n\n        // clear map\n        bdmap_.clear();\n\n        try {\n\n            // iterate through tasks using taskmodel\n            Collection<Address> addrs = getAddresses();\n            for (Address addr : addrs) {\n\n                // use birthday to build a day key\n                Date bd = addr.getBirthday();\n                if (bd == null)\n                    continue;\n\n                int bdkey = birthdayKey(bd);\n\n                // add the task string to the btmap_\n                // add the task to the mrs_ Vector. This is used by the todo gui\n                LinkedList<Address> o = bdmap_.get(Integer.valueOf(bdkey));\n                if (o == null) {\n                    o = new LinkedList<Address>();\n                    bdmap_.put(Integer.valueOf(bdkey), o);\n                }\n\n                o.add(addr);\n            }\n\n        } catch (Exception e) {\n\n            Errmsg.getErrorHandler().errmsg(e);\n            return;\n        }\n\n    }\n\n    /**\n     * get a new address object\n     *\n     * @return the address\n     */\n    public Address newAddress() {\n        return (db_.newObj());\n    }\n\n    /**\n     * Refresh the birthday map and notify any listeners that the model has\n     * changed\n     */\n    public void refresh() {\n        load_map();\n        refreshListeners();\n    }\n\n    /**\n     * Save an address.\n     *\n     * @param addr the address\n     * @throws Exception\n     */\n    public void saveAddress(Address addr) throws Exception {\n        saveAddress(addr, false);\n    }\n\n    /**\n     * Save an address.\n     *\n     * @param addr the address\n     * @param undo true if we are executing an undo\n     * @throws Exception\n     */\n    public void saveAddress(Address addr, boolean undo) throws Exception {\n\n        int num = addr.getKey();\n\n        Address orig_addr = getAddress(addr.getKey());\n        if (num == -1 || orig_addr == null) {\n\n            int newkey = db_.nextkey();\n            if (undo && num != -1 && orig_addr == null) {\n                newkey = num;\n            }\n            addr.setKey(newkey);\n            db_.addObj(addr);\n            if (!undo) {\n                UndoLog.getReference().addItem(\n                        AddressUndoItem.recordAdd(addr));\n            }\n        } else {\n            db_.updateObj(addr);\n            if (!undo) {\n                UndoLog.getReference().addItem(\n                        AddressUndoItem.recordUpdate(orig_addr));\n            }\n        }\n\n\n        // inform views of data change\n        refresh();\n    }\n\n    /**\n     * Sync with the underlying db\n     */\n    @Override\n    public void sync() {\n        db_.sync();\n        refresh();\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see\n     * net.sf.borg.model.Searchable#search(net.sf.borg.model.SearchCriteria)\n     */\n    @Override\n    public Collection<Address> search(SearchCriteria criteria) {\n        Collection<Address> res = new ArrayList<Address>(); // result collection\n        try {\n\n            // do not match if a search category is set\n            if (!criteria.getCategory().equals(\"\")\n                    && !criteria.getCategory().equals(\n                    CategoryModel.UNCATEGORIZED))\n                return res;\n\n            // load all addresses into appt list\n            Collection<Address> addresses = getAddresses();\n\n            for (Address addr : addresses) {\n                // read each appt\n\n                String addrString = addr.getFirstName() + \" \"\n                        + addr.getLastName() + \" \" + addr.getStreetAddress()\n                        + \" \" + addr.getWorkStreetAddress() + \" \"\n                        + addr.getCompany() + \" \" + addr.getCity() + \" \"\n                        + addr.getCountry() + \" \" + addr.getWorkCity() + \" \"\n                        + addr.getWorkCountry() + \" \" + addr.getEmail() + \" \"\n                        + addr.getState() + \" \" + addr.getWorkState() + \" \"\n                        + addr.getNickname() + \" \" + addr.getCellPhone();\n\n                if (!criteria.search(addrString))\n                    continue;\n\n                // filter by links\n                if (criteria.hasLinks()) {\n                    LinkModel lm = LinkModel.getReference();\n                    try {\n                        Collection<Link> lnks = lm.getLinks(addr);\n                        if (lnks.isEmpty())\n                            continue;\n                    } catch (Exception e) {\n                        Errmsg.getErrorHandler().errmsg(e);\n                    }\n                }\n\n                // add the appt to the search results\n                res.add(addr);\n\n            }\n\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n        return (res);\n    }\n\n    @Override\n    public String getExportName() {\n        return \"ADDRESSES\";\n    }\n\n    @Override\n    public String getInfo() throws Exception {\n        return Resource.getResourceString(\"addresses\") + \": \"\n                + getAddresses().size();\n    }\n\n    @Override\n    public List<CalendarEntity> getEntities(Date d) {\n\n        List<CalendarEntity> ret = new ArrayList<CalendarEntity>();\n        // add birthdays from address book\n        GregorianCalendar gc = new GregorianCalendar();\n        gc.setTime(d);\n        Collection<Address> addrs = AddressModel.getReference().getAddresses(d);\n        if (addrs != null) {\n            for (Address addr : addrs) {\n                LabelEntity info = new LabelEntity();\n                String color = info.getColor();\n\n                if (color == null)\n                    info.setColor(Theme.BIRTHDAYCOLOR);\n\n                Date bd = addr.getBirthday();\n                GregorianCalendar g = new GregorianCalendar();\n                g.setTime(bd);\n                int bdyear = g.get(Calendar.YEAR);\n                int yrs = gc.get(Calendar.YEAR) - bdyear;\n                if (yrs < 0)\n                    continue;\n\n                String tx = Resource.getResourceString(\"Birthday\") + \": \"\n                        + addr.getFirstName() + \" \" + addr.getLastName() + \"(\"\n                        + yrs + \")\";\n                info.setText(tx);\n                info.setDate(d);\n                ret.add(info);\n            }\n        }\n\n        return ret;\n\n    }\n\n\n    public int removeDuplicates() throws Exception {\n        int dels = 0;\n        Set<Address> addressSet = new HashSet<Address>();\n        for (Address addr : getAddresses()) {\n            if (addressSet.add(addr) == false){\n                delete(addr);\n                dels++;\n            }\n        }\n\n        return dels;\n\n    }\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/AppointmentModel.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003-2010 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.*;\r\nimport net.sf.borg.model.CategoryModel.CategorySource;\r\nimport net.sf.borg.model.db.AppointmentDB;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.EntityDB;\r\nimport net.sf.borg.model.entity.Appointment;\r\nimport net.sf.borg.model.entity.Link;\r\nimport net.sf.borg.model.undo.AppointmentUndoItem;\r\nimport net.sf.borg.model.undo.UndoLog;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.StringReader;\r\nimport java.io.StringWriter;\r\nimport java.io.Writer;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.*;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * the appointment model provides the model layer APIs for working with\r\n * Appointment Entities\r\n * \r\n */\r\npublic class AppointmentModel extends Model implements Model.Listener, CategorySource, Searchable<Appointment> {\r\n\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t/**\r\n\t * class XmlContainer is solely for JAXB XML export/import to keep the same XML\r\n\t * structure as before JAXB was used\r\n\t */\r\n\t@XmlRootElement(name = \"APPTS\")\r\n\tprivate static class XmlContainer {\r\n\t\tpublic Collection<Appointment> Appointment;\r\n\t}\r\n\r\n\t/** The singleton */\r\n\tstatic private final AppointmentModel self_ = new AppointmentModel();\r\n\r\n\t/**\r\n\t * Gets the singleton reference.\r\n\t * \r\n\t * @return the singleton reference\r\n\t */\r\n\tpublic static AppointmentModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the time format to use for all time processing.\r\n\t * \r\n\t * @return the time format\r\n\t */\r\n\tpublic static SimpleDateFormat getTimeFormat() {\r\n\t\tString mt = Prefs.getPref(PrefName.MILTIME);\r\n\t\tif (mt.equals(\"true\")) {\r\n\t\t\treturn (new SimpleDateFormat(\"HH:mm\"));\r\n\t\t}\r\n\r\n\t\treturn (new SimpleDateFormat(\"h:mm a\"));\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Checks an appointment is a note (not associated with a time of day).\r\n\t * \r\n\t * @param appt\r\n\t *            the appointment\r\n\t * \r\n\t * @return true, if it is a note\r\n\t */\r\n\tpublic static boolean isNote(Appointment appt) {\r\n\t\t// return true if the appt Appointment represents a \"note\" or\r\n\t\t// \"non-timed\" appt\r\n\t\treturn appt.getUntimed() != null && appt.getUntimed().equals(\"Y\");\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Checks if an appointment is skipped on a particular date\r\n\t * \r\n\t * @param ap\r\n\t *            the Appointment\r\n\t * @param cal\r\n\t *            the date\r\n\t * \r\n\t * @return true, if is skipped\r\n\t */\r\n\tpublic static boolean isSkipped(Appointment ap, Calendar cal) {\r\n\t\tint dk = DateUtil.dayOfEpoch(cal.getTime());\r\n\t\tString sk = Integer.toString(dk);\r\n\t\tVector<String> skv = ap.getSkipList();\r\n\t\treturn skv != null && skv.contains(sk);\r\n\t}\r\n\r\n\t/** The underlying database object */\r\n\tprivate final EntityDB<Appointment> db_;\r\n\r\n\t/**\r\n\t * map_ contains each \"base\" day key that has appts and maps it to a list of\r\n\t * appt keys for that day.\r\n\t */\r\n\tprivate final HashMap<Integer, Collection<Integer>> map_;\r\n\r\n\t/**\r\n\t * The vacation map - a map of vacation values for each day (i.e. no vacation,\r\n\t * half-day, full-day)\r\n\t */\r\n\tprivate final HashMap<Integer, Integer> vacationMap_;\r\n\r\n\t/**\r\n\t * Instantiates a new appointment model.\r\n\t * \r\n\t */\r\n\tprivate AppointmentModel() {\r\n\t\tmap_ = new HashMap<Integer, Collection<Integer>>();\r\n\t\tvacationMap_ = new HashMap<Integer, Integer>();\r\n\t\tdb_ = DBHelper.getFactory().createAppointmentDB();\r\n\r\n\t\t// init categories and currentcategories\r\n\t\tCategoryModel.getReference().addSource(this);\r\n\t\tCategoryModel.getReference().addListener(this);\r\n\r\n\t\t// scan the DB and build the appt map_\r\n\t\ttry {\r\n\t\t\tbuildMap();\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Builds the map (cache) of days to appointments. Also builds the vacation map.\r\n\t * This is not purely for caching as it also maps multiple days to a single\r\n\t * appointment to support repeating appointments\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tprivate void buildMap() throws Exception {\r\n\t\t// erase the current map\r\n\t\tmap_.clear();\r\n\t\tvacationMap_.clear();\r\n\r\n\t\t// get the year for later\r\n\t\tGregorianCalendar cal = new GregorianCalendar();\r\n\t\tint curyr = cal.get(Calendar.YEAR);\r\n\r\n\t\t// scan entire DB\r\n\t\tAppointmentDB kf = (AppointmentDB) db_;\r\n\t\tCollection<Integer> rptkeys = kf.getRepeatKeys();\r\n\r\n\t\tfor (Appointment appt : getAllAppts()) {\r\n\r\n\t\t\tif (!CategoryModel.getReference().isShown(appt.getCategory())) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tint dkey = DateUtil.dayOfEpoch(appt.getDate());\r\n\r\n\t\t\t// if appt does not repeat, we can add its\r\n\t\t\t// key to a single day\r\n\t\t\tint key = appt.getKey();\r\n\t\t\tInteger ki = Integer.valueOf(key);\r\n\t\t\tif (!rptkeys.contains(ki)) {\r\n\r\n\t\t\t\t// get/add entry for the day in the map\r\n\t\t\t\tCollection<Integer> o = map_.get(Integer.valueOf(dkey));\r\n\t\t\t\tif (o == null) {\r\n\t\t\t\t\to = new LinkedList<Integer>();\r\n\t\t\t\t\tmap_.put(Integer.valueOf(dkey), o);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// add the appt key to the day's list\r\n\t\t\t\to.add(Integer.valueOf(key));\r\n\r\n\t\t\t\t// add day key to vacation map if appt has vacation\r\n\t\t\t\tif (appt.getVacation() != null && appt.getVacation().intValue() != 0) {\r\n\t\t\t\t\tvacationMap_.put(Integer.valueOf(dkey), appt.getVacation());\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\r\n\t\t\t\tcal.setTime(appt.getDate());\r\n\r\n\t\t\t\tRepeat repeat = new Repeat(cal, appt.getFrequency());\r\n\t\t\t\tif (!repeat.isRepeating())\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tint tm = Repeat.calculateTimes(appt);\r\n\r\n\t\t\t\tint apptYear = cal.get(Calendar.YEAR);\r\n\r\n\t\t\t\t// ok, plod through the repeats now\r\n\t\t\t\tfor (int i = 0; i < tm; i++) {\r\n\t\t\t\t\tCalendar current = repeat.current();\r\n\t\t\t\t\tif (current == null) {\r\n\t\t\t\t\t\trepeat.next();\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t// get the day key for the repeat\r\n\t\t\t\t\tint rkey = DateUtil.dayOfEpoch(current.getTime());\r\n\r\n\t\t\t\t\tint cyear = current.get(Calendar.YEAR);\r\n\r\n\t\t\t\t\t// limit the repeats to 10 years\r\n\t\t\t\t\tif (cyear > curyr + 10 && cyear > apptYear + 10)\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t// check if the repeat is in the skip list\r\n\t\t\t\t\t// if so, skip it\r\n\t\t\t\t\tif (!isSkipped(appt, current)) {\r\n\t\t\t\t\t\t// add the repeat key to the map\r\n\t\t\t\t\t\tCollection<Integer> o = map_.get(Integer.valueOf(rkey));\r\n\t\t\t\t\t\tif (o == null) {\r\n\t\t\t\t\t\t\to = new LinkedList<Integer>();\r\n\t\t\t\t\t\t\tmap_.put(Integer.valueOf(rkey), o);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tLinkedList<Integer> l = (LinkedList<Integer>) o;\r\n\t\t\t\t\t\tl.add(Integer.valueOf(key));\r\n\r\n\t\t\t\t\t\t// add day key to vacation map if appt has vacation\r\n\t\t\t\t\t\tif (appt.getVacation() != null && appt.getVacation().intValue() != 0) {\r\n\t\t\t\t\t\t\tvacationMap_.put(Integer.valueOf(rkey), appt.getVacation());\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\trepeat.next();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Delete an appt.\r\n\t * \r\n\t * @param appt\r\n\t *            the appt\r\n\t */\r\n\tpublic void delAppt(Appointment appt) {\r\n\t\tdelAppt(appt, false);\r\n\t}\r\n\r\n\t/**\r\n\t * Delete an appt.\r\n\t * \r\n\t * @param appt\r\n\t *            the appt\r\n\t * @param undo\r\n\t *            true if we are executing an undo\r\n\t */\r\n\tpublic void delAppt(Appointment appt, boolean undo) {\r\n\r\n\t\tAppointment orig_appt = null;\r\n\t\ttry {\r\n\r\n\t\t\torig_appt = getAppt(appt.getKey());\r\n\r\n\t\t\tLinkModel.getReference().deleteLinksFromEntity(appt);\r\n\t\t\tLinkModel.getReference().deleteLinksToEntity(appt);\r\n\r\n\t\t\tdb_.delete(appt.getKey());\r\n\t\t\tif (!undo) {\r\n\t\t\t\tUndoLog.getReference().addItem(AppointmentUndoItem.recordDelete(orig_appt));\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\t// even if delete fails - still refresh cache info\r\n\t\t// and tell listeners - db failure may have been due to\r\n\t\t// a sync causing a record already deleted error\r\n\t\t// this needs to be reflected in the map\r\n\t\ttry {\r\n\t\t\t// recreate the appointment hashmap\r\n\t\t\tbuildMap();\r\n\r\n\t\t\t// refresh all views that are displaying appt data from this\r\n\t\t\t// model\r\n\t\t\trefreshListeners(new ChangeEvent(appt, ChangeEvent.ChangeAction.DELETE));\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Delete an appt by key.\r\n\t * \r\n\t * @param key\r\n\t *            the key\r\n\t */\r\n\tpublic void delAppt(int key) {\r\n\t\ttry {\r\n\t\t\tAppointment appt = getAppt(key);\r\n\t\t\tif (appt != null)\r\n\t\t\t\tdelAppt(appt);\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * delete one occurrence of a repeating appointment\r\n\t * \r\n\t * @param key\r\n\t *            the appointment key\r\n\t * @param rptDate\r\n\t *            the date of the repeat to be deleted\r\n\t */\r\n\tpublic void delOneOnly(int key, Date rptDate) {\r\n\t\ttry {\r\n\r\n\t\t\tint rkey = DateUtil.dayOfEpoch(rptDate);\r\n\r\n\t\t\tAppointment appt = db_.readObj(key);\r\n\r\n\t\t\t// get the list of repeats that have been deleted - the SKip\r\n\t\t\t// list\r\n\t\t\tVector<String> vect = appt.getSkipList();\r\n\t\t\tif (vect == null)\r\n\t\t\t\tvect = new Vector<String>();\r\n\r\n\t\t\t// add the current appt key to the SKip list\r\n\t\t\tvect.add(Integer.toString(rkey));\r\n\t\t\tappt.setSkipList(vect);\r\n\t\t\tsaveAppt(appt, false);\r\n\r\n\t\t\t// if we are deleting the next todo then do it\r\n\t\t\tDate nt = appt.getNextTodo();\r\n\t\t\tif (nt == null)\r\n\t\t\t\tnt = appt.getDate();\r\n\r\n\t\t\tif (rkey == DateUtil.dayOfEpoch(nt)) {\r\n\t\t\t\tdo_todo(appt.getKey(), false);\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Mark a todo appointment as done. If the appointment repeats, adjust the next\r\n\t * todo value. If the todo is all done (including repeats), optionally delete it\r\n\t * \r\n\t * @param key\r\n\t *            the appointment key\r\n\t * @param del\r\n\t *            if true, delete the todo when all done. Otherwise, mark it as no\r\n\t *            longer being a todo.\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void do_todo(int key, boolean del) throws Exception {\r\n\t\tthis.do_todo(key, del, null);\r\n\t}\r\n\r\n\t/**\r\n\t * find the date of the next un-skipped todo occurrence, if any\r\n\t * \r\n\t * @param appt - the appointment\r\n\t * \r\n\t * @param date - the date of the occurrence to start from. if null, the next one is used.\r\n\t * \r\n\t * @return date of the next todo occurrence or null if none\r\n\t */\r\n\tpublic Date next_todo(Appointment appt, Date date) {\r\n\t\t\r\n\t\tif( !appt.isTodo()) return null;\r\n\t\t\r\n\t\t// curtodo is the date of the todo that is to be \"done\"\r\n\t\tDate curtodo = appt.getNextTodo();\r\n\t\tDate d = appt.getDate();\r\n\t\tif (curtodo == null) {\r\n\t\t\tcurtodo = d;\r\n\t\t}\r\n\r\n\t\tif (date != null)\r\n\t\t\tcurtodo = date;\r\n\r\n\t\t// newtodo will be the name of the next todo occurrence (if the todo\r\n\t\t// repeats and is not done)\r\n\t\tDate newtodo = null;\r\n\r\n\t\tint tm = Repeat.calculateTimes(appt);\r\n\t\tString rpt = Repeat.getFreq(appt.getFrequency());\r\n\r\n\t\t// find next to do if it repeats by doing calendar math\r\n\t\tif (tm > 1 && rpt != null && Repeat.isRepeating(appt)) {\r\n\r\n\t\t\tCalendar ccal = new GregorianCalendar();\r\n\t\t\tCalendar ncal = new GregorianCalendar();\r\n\r\n\t\t\t// ccal is the current todo and ncal is the original appt date\r\n\t\t\t// ncal will be incremented until we find the todo after the\r\n\t\t\t// one in ccal\r\n\t\t\tccal.setTime(curtodo);\r\n\t\t\tncal.setTime(d);\r\n\r\n\t\t\tRepeat repeat = new Repeat(ncal, appt.getFrequency());\r\n\t\t\tfor (int i = 1; i < tm; i++) {\r\n\r\n\t\t\t\tif (ncal != null && ncal.get(Calendar.YEAR) == ccal.get(Calendar.YEAR)\r\n\t\t\t\t\t\t&& ncal.get(Calendar.MONTH) == ccal.get(Calendar.MONTH)\r\n\t\t\t\t\t\t&& ncal.get(Calendar.DATE) == ccal.get(Calendar.DATE)) {\r\n\r\n\t\t\t\t\twhile (true) {\r\n\t\t\t\t\t\tncal = repeat.next();\r\n\t\t\t\t\t\tif (ncal == null)\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tif (isSkipped(appt, ncal))\r\n\t\t\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t\t\tnewtodo = ncal.getTime();\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tncal = repeat.next();\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\treturn newtodo;\r\n\t}\r\n\r\n\t/**\r\n\t * Mark a todo appointment as done. If the appointment repeats, adjust the next\r\n\t * todo value. If the todo is all done (including repeats), optionally delete it\r\n\t * \r\n\t * @param key\r\n\t *            the appointment key\r\n\t * @param del\r\n\t *            if true, delete the todo when all done. Otherwise, mark it as no\r\n\t *            longer being a todo.\r\n\t * @param date\r\n\t *            date of the repeat that is being marked as done. If null, then the\r\n\t *            next todo is the one. If set, then all todos up to and including\r\n\t *            the date are marked as done.\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void do_todo(int key, boolean del, Date date) throws Exception {\r\n\t\t// read the DB row for the ToDo\r\n\t\tAppointment appt = db_.readObj(key);\r\n\t\t\r\n\t\tDate newtodo = next_todo(appt, date);\r\n\r\n\t\tif (newtodo != null) {\r\n\t\t\t// a next todo was found, set NT to that value\r\n\t\t\t// and don't delete the appt\r\n\t\t\tappt.setNextTodo(newtodo);\r\n\t\t\tsaveAppt(appt, false);\r\n\t\t} else {\r\n\t\t\t// there is no next todo - shut off the todo\r\n\t\t\t// unless the user wants it deleted. if so, delete it.\r\n\t\t\tif (del) {\r\n\t\t\t\tdelAppt(appt);\r\n\t\t\t} else {\r\n\t\t\t\t// this needs to be a delete and add for caldav since a task is being changed to an event and caldasv won't delete the task\r\n\t\t\t\tAppointment newEvent = appt.copy();\r\n\t\t\t\tnewEvent.setKey(-1);\r\n\t\t\t\tnewEvent.setUid(null);\r\n\t\t\t\tnewEvent.setTodo(false);\r\n\t\t\t\tnewEvent.setColor(\"strike\"); // strike the text to indicate a done\r\n\t\t\t\t// todo\r\n\t\t\t\tsaveAppt(newEvent, false);\r\n\t\t\t\tdelAppt(appt);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Export appointments as XML.\r\n\t * \r\n\t * @param fw\r\n\t *            the Writer to write XML to\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tMarshaller m = jc.createMarshaller();\r\n\t\tXmlContainer container = new XmlContainer();\r\n\t\tcontainer.Appointment = getAllAppts();\r\n\t\tm.marshal(container, fw);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all appointments that are marked as todos.\r\n\t * \r\n\t * @return the todos\r\n\t */\r\n\tpublic Collection<Appointment> get_todos() {\r\n\r\n\t\tArrayList<Appointment> av = new ArrayList<Appointment>();\r\n\t\ttry {\r\n\r\n\t\t\t// iterate through appts in the DB\r\n\t\t\tAppointmentDB kf = (AppointmentDB) db_;\r\n\t\t\tCollection<Integer> keycol = kf.getTodoKeys();\r\n\t\t\t// Collection keycol = AppointmentHelper.getTodoKeys(db_);\r\n\t\t\tfor (Integer ki : keycol) {\r\n\t\t\t\tint key = ki.intValue();\r\n\r\n\t\t\t\t// read the full appt from the DB and add to the vector\r\n\t\t\t\tAppointment appt = db_.readObj(key);\r\n\r\n\t\t\t\t// if category set, filter appts\r\n\t\t\t\tif (!CategoryModel.getReference().isShown(appt.getCategory())) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tav.add(appt);\r\n\t\t\t}\r\n\t\t} catch (Exception ee) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(ee);\r\n\t\t}\r\n\r\n\t\treturn (av);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Get all appts.\r\n\t * \r\n\t * @return all appts\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Collection<Appointment> getAllAppts() throws Exception {\r\n\t\tCollection<Appointment> appts = db_.readAll();\r\n\t\treturn appts;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets an appt by key.\r\n\t * \r\n\t * @param key\r\n\t *            the key\r\n\t * \r\n\t * @return the appt\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Appointment getAppt(int key) throws Exception {\r\n\t\tAppointment appt = db_.readObj(key);\r\n\t\treturn (appt);\r\n\t}\r\n\r\n\t/**\r\n\t * Get a list of appointment ids for a given day\r\n\t * \r\n\t * @param d\r\n\t *            the date\r\n\t * \r\n\t * @return the appts\r\n\t */\r\n\tpublic List<Integer> getAppts(Date d) {\r\n\t\treturn ((List<Integer>) map_.get(Integer.valueOf(DateUtil.dayOfEpoch(d))));\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.CategoryModel.CategorySource#getCategories()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<String> getCategories() {\r\n\r\n\t\tTreeSet<String> dbcat = new TreeSet<String>();\r\n\t\tdbcat.add(CategoryModel.UNCATEGORIZED);\r\n\t\ttry {\r\n\t\t\tfor (Appointment ap : getAllAppts()) {\r\n\t\t\t\tString cat = ap.getCategory();\r\n\t\t\t\tif (cat != null && !cat.equals(\"\"))\r\n\t\t\t\t\tdbcat.add(cat);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\treturn (dbcat);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the underlying dB.\r\n\t * \r\n\t * @return the dB\r\n\t */\r\n\t@Deprecated\r\n\tpublic EntityDB<Appointment> getDB() {\r\n\t\treturn (db_);\r\n\t}\r\n\r\n\t/**\r\n\t * Import xml.\r\n\t * \r\n\t * @param is\r\n\t *            the input stream containing the XML\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\r\n\t\tXmlContainer container = (XmlContainer) u.unmarshal(is);\r\n\r\n\t\tif (container.Appointment == null)\r\n\t\t\treturn;\r\n\r\n\t\ttry {\r\n\t\t\t// use key from import file if importing into empty db\r\n\t\t\tint nextkey = db_.nextkey();\r\n\t\t\tboolean use_keys = nextkey == 1;\r\n\t\t\tfor (Appointment appt : container.Appointment) {\r\n\t\t\t\tif (!use_keys)\r\n\t\t\t\t\tappt.setKey(nextkey++);\r\n\t\t\t\t// validate priority\r\n\t\t\t\tif (appt.getPriority() != null && (appt.getPriority() > 10 || appt.getPriority() < 1)) {\r\n\t\t\t\t\tappt.setPriority(null);\r\n\t\t\t\t\tlog.warning(\"XML Import: Ignoring invalid priority for appointment: \" + appt.getKey() + \":\"\r\n\t\t\t\t\t\t\t+ appt.getClearText());\r\n\t\t\t\t}\r\n\t\t\t\tif (appt.getCreateTime() == null)\r\n\t\t\t\t\tappt.setCreateTime(new Date());\r\n\t\t\t\tif (appt.getLastMod() == null)\r\n\t\t\t\t\tappt.setLastMod(new Date());\r\n\t\t\t\tdb_.addObj(appt);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().notice(e + \"\\n\" + Resource.getResourceString(\"Import_XML_error\"));\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\r\n\t\t// rebuild the hashmap\r\n\t\tbuildMap();\r\n\r\n\t\tCategoryModel.getReference().sync();\r\n\r\n\t\t// refresh all views that are displaying appt data from this model\r\n\t\trefreshListeners();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * create a new appointment.\r\n\t * \r\n\t * @return the appointment\r\n\t */\r\n\tpublic Appointment newAppt() {\r\n\t\tAppointment appt = db_.newObj();\r\n\t\treturn (appt);\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.Model.Listener#refresh()\r\n\t */\r\n\t@Override\r\n\tpublic void update(ChangeEvent event) {\r\n\t\trefresh();\r\n\t}\r\n\r\n\tpublic void refresh() {\r\n\t\ttry {\r\n\t\t\tbuildMap();\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\t// refresh all views that are displaying appt data from this model\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Save an appointment.\r\n\t * \r\n\t * @param r\r\n\t *            the appointment\r\n\t */\r\n\tpublic void saveAppt(Appointment r) {\r\n\t\tsaveAppt(r, false);\r\n\t}\r\n\r\n\t/**\r\n\t * Save an appointment.\r\n\t * \r\n\t * @param r\r\n\t *            the appointment\r\n\t * @param undo\r\n\t *            true if we are executing an undo\r\n\t */\r\n\tpublic void saveAppt(Appointment r, boolean undo) {\r\n\r\n\t\tChangeEvent.ChangeAction action = ChangeEvent.ChangeAction.ADD;\r\n\t\ttry {\r\n\t\t\tAppointment orig_appt = null;\r\n\t\t\tif (r.getKey() != -1)\r\n\t\t\t\torig_appt = getAppt(r.getKey());\r\n\t\t\tif (orig_appt == null) {\r\n\r\n\t\t\t\t// if undo is adding back record - force the key to\r\n\t\t\t\t// be what is in the record\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tr.setKey(db_.nextkey());\r\n\t\t\t\t}\r\n\r\n\t\t\t\tr.setCreateTime(new Date());\r\n\t\t\t\tr.setLastMod(r.getCreateTime());\r\n\t\t\t\tif (r.getUid() == null)\r\n\t\t\t\t\tr.setUid(r.getKey() + \"@BORGA-\" + r.getLastMod().getTime());\r\n\t\t\t\tdb_.addObj(r);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference().addItem(AppointmentUndoItem.recordAdd(r));\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\r\n\t\t\t\tif (r.getCreateTime() == null)\r\n\t\t\t\t\tr.setCreateTime(new Date());\r\n\r\n\t\t\t\tr.setLastMod(new Date());\r\n\t\t\t\tif (r.getUid() == null)\r\n\t\t\t\t\tr.setUid(r.getKey() + \"@BORGA-\" + r.getLastMod().getTime());\r\n\t\t\t\taction = ChangeEvent.ChangeAction.CHANGE;\r\n\t\t\t\tdb_.updateObj(r);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference().addItem(AppointmentUndoItem.recordUpdate(orig_appt));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// update category list\r\n\t\t\tString cat = r.getCategory();\r\n\t\t\tif (cat != null && !cat.equals(\"\"))\r\n\t\t\t\tCategoryModel.getReference().addCategory(cat);\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\t// recreate the appointment hashmap\r\n\t\t\tbuildMap();\r\n\r\n\t\t\t// refresh all views that are displaying appt data from this\r\n\t\t\t// model\r\n\t\t\trefreshListeners(new ChangeEvent(r, action));\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Sync with the db.\r\n\t */\r\n\t@Override\r\n\tpublic void sync() {\r\n\t\tdb_.sync();\r\n\t\ttry {\r\n\t\t\t// recreate the appointment hashmap\r\n\t\t\tbuildMap();\r\n\r\n\t\t\t// refresh all views that are displaying appt data from this\r\n\t\t\t// model\r\n\t\t\trefreshListeners();\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * determine the number of vacation days up to and including the given day in a\r\n\t * particular year\r\n\t * \r\n\t * @param d\r\n\t *            the Date\r\n\t * \r\n\t * @return the double\r\n\t */\r\n\tpublic double vacationCount(Date d) {\r\n\r\n\t\tCalendar cal = new GregorianCalendar();\r\n\t\tcal.setTime(d);\r\n\t\tcal.set(Calendar.DATE, 1);\r\n\t\tcal.set(Calendar.MONTH, Calendar.JANUARY);\r\n\t\tint dk = DateUtil.dayOfEpoch(d);\r\n\t\tint yearStartKey = DateUtil.dayOfEpoch(cal.getTime());\r\n\t\tdouble count = 0;\r\n\r\n\t\tSet<Integer> vkeys = vacationMap_.keySet();\r\n\t\tfor (Integer i : vkeys) {\r\n\t\t\tint vdaykey = i.intValue();\r\n\t\t\tif (vdaykey >= yearStartKey && vdaykey <= dk) {\r\n\t\t\t\tInteger vnum = vacationMap_.get(i);\r\n\t\t\t\tif (vnum.intValue() == 2) {\r\n\t\t\t\t\tcount += 0.5;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcount += 1.0;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn count;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * save the default appointment in the prefs\r\n\t * \r\n\t * @param appt\r\n\t *            the appointment\r\n\t */\r\n\tpublic void saveDefaultAppointment(Appointment appt) {\r\n\r\n\t\ttry {\r\n\t\t\tJAXBContext jc = JAXBContext.newInstance(Appointment.class);\r\n\t\t\tMarshaller m = jc.createMarshaller();\r\n\t\t\tStringWriter sw = new StringWriter();\r\n\t\t\tm.marshal(appt, sw);\r\n\t\t\tPrefs.putPref(PrefName.DEFAULT_APPT, sw.toString());\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * get the default appointment from prefs\r\n\t * \r\n\t * @return the default appointment\r\n\t */\r\n\tpublic static Appointment getDefaultAppointment() {\r\n\r\n\t\tString defApptXml = Prefs.getPref(PrefName.DEFAULT_APPT);\r\n\t\tif (!defApptXml.equals(\"\")) {\r\n\t\t\ttry {\r\n\t\t\t\tJAXBContext jc = JAXBContext.newInstance(Appointment.class);\r\n\t\t\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\t\t\tString xmlString = defApptXml;\r\n\t\t\t\tAppointment ap = (Appointment) u.unmarshal(new StringReader(xmlString));\r\n\r\n\t\t\t\t// transition from pre-1.7.2\r\n\t\t\t\tif (ap.getDate() == null)\r\n\t\t\t\t\treturn null;\r\n\t\t\t\treturn ap;\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.Searchable#search(net.sf.borg.model.SearchCriteria)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Appointment> search(SearchCriteria criteria) {\r\n\r\n\t\tCollection<Appointment> res = new ArrayList<Appointment>(); // result\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// collection\r\n\t\ttry {\r\n\r\n\t\t\t// load all appts into appt list\r\n\t\t\tCollection<Appointment> allappts = getAllAppts();\r\n\r\n\t\t\tfor (Appointment appt : allappts) {\r\n\t\t\t\t// read each appt\r\n\r\n\t\t\t\t// if category set, filter appts\r\n\t\t\t\tif (!CategoryModel.getReference().isShown(appt.getCategory())) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// do not search on encrypted appts\r\n\t\t\t\tif (appt.isEncrypted())\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tString tx = appt.getText();\r\n\t\t\t\tDate d = appt.getDate();\r\n\t\t\t\tif (d == null || tx == null)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tif (!criteria.search(tx))\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter by repeat\r\n\t\t\t\tif (criteria.isRepeating() && !appt.isRepeatFlag())\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter todos\r\n\t\t\t\tif (criteria.isTodo() && !appt.isTodo())\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter by vacation\r\n\t\t\t\tInteger ii = appt.getVacation();\r\n\t\t\t\tif (criteria.isVacation() && (ii == null || ii.intValue() != 1))\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter by holiday\r\n\t\t\t\tii = appt.getHoliday();\r\n\t\t\t\tif (criteria.isHoliday() && (ii == null || ii.intValue() != 1))\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter by category\r\n\t\t\t\tif (criteria.getCategory().equals(CategoryModel.UNCATEGORIZED) && appt.getCategory() != null\r\n\t\t\t\t\t\t&& !appt.getCategory().equals(CategoryModel.UNCATEGORIZED))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\telse if (!criteria.getCategory().equals(\"\")\r\n\t\t\t\t\t\t&& !criteria.getCategory().equals(CategoryModel.UNCATEGORIZED)\r\n\t\t\t\t\t\t&& !criteria.getCategory().equals(appt.getCategory()))\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t// filter by start date\r\n\t\t\t\tif (criteria.getStartDate() != null) {\r\n\t\t\t\t\tif (appt.getDate().before(criteria.getStartDate()))\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// filter by end date\r\n\t\t\t\tif (criteria.getEndDate() != null) {\r\n\t\t\t\t\tif (appt.getDate().after(criteria.getEndDate()))\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// filter by links\r\n\t\t\t\tif (criteria.hasLinks()) {\r\n\t\t\t\t\tLinkModel lm = LinkModel.getReference();\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tCollection<Link> lnks = lm.getLinks(appt);\r\n\t\t\t\t\t\tif (lnks.isEmpty())\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// add the appt to the search results\r\n\t\t\t\tres.add(appt);\r\n\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t\treturn (res);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn \"APPTS\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"appointments\") + \": \" + getAllAppts().size();\r\n\t}\r\n\r\n\tpublic List<Appointment> getAppointmentsByText(String text) throws Exception {\r\n\t\tAppointmentDB apdb = (AppointmentDB) db_;\r\n\t\treturn apdb.getAppointmentsByText(text);\r\n\t}\r\n\r\n\tpublic Appointment getApptByUid(String uid) throws Exception {\r\n\t\tAppointmentDB apdb = (AppointmentDB) db_;\r\n\t\treturn apdb.getAppointmentByUid(uid);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/CalendarEntityProvider.java",
    "content": "package net.sf.borg.model;\n\nimport net.sf.borg.model.entity.CalendarEntity;\n\nimport java.util.Date;\nimport java.util.List;\n\npublic interface CalendarEntityProvider {\n\t\n\tList<CalendarEntity> getEntities(Date d);\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/CategoryModel.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport net.sf.borg.common.Resource;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Serializable;\r\nimport java.io.Writer;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.Comparator;\r\nimport java.util.TreeSet;\r\n\r\n/**\r\n * The Class CategoryModel manages Categories. Categories are not entities, they are plain text strings.\r\n * Categories do not exist outside of other Entities. This class maintains a cache of category information\r\n * in memory but does not persist it. It recreates this information from the category-aware models as needed.\r\n */\r\npublic class CategoryModel extends Model {\r\n\r\n\t/**\r\n\t * interface implemented by Models whose entities contain categories\r\n\t */\r\n\tinterface CategorySource {\r\n\t\t\r\n\t\t/**\r\n\t\t * Gets the list of all categories from all entities in the source model\r\n\t\t * \r\n\t\t * @return the categories\r\n\t\t */\r\n\t\tCollection<String> getCategories();\r\n\t}\r\n\r\n\tstatic class CategoryComparator implements Comparator<String>, Serializable\r\n\t{\r\n\r\n\t\tprivate static final long serialVersionUID = 1L;\r\n\r\n\t\t@Override\r\n\t\tpublic int compare(String o1, String o2) {\r\n\t\t\tif( o1.compareTo(o2) == 0)\r\n\t\t\t\treturn 0;\r\n\t\t\tif( o1.equals(UNCATEGORIZED))\r\n\t\t\t\treturn -1;\r\n\t\t\tif( o2.equals(UNCATEGORIZED))\r\n\t\t\t\treturn 1;\r\n\t\t\treturn( o1.compareTo(o2));\r\n\t\t}\r\n\t\t\r\n\t}\r\n\t\r\n\t/** The singleton */\r\n\tstatic private final CategoryModel self_ = new CategoryModel();\r\n\r\n\t/** a non-null value to represent the lack of a category */\r\n\tstatic public final String UNCATEGORIZED = Resource\r\n\t\t\t.getResourceString(\"uncategorized\");\r\n\r\n\t/**\r\n\t * Gets the singleton reference.\r\n\t * \r\n\t * @return the reference\r\n\t */\r\n\tpublic static CategoryModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\t/** The collection of all categories_. */\r\n\tprivate final Collection<String> categories_ = new TreeSet<String>(new CategoryComparator());\r\n\r\n\t/** The categories that are being shown (i.e. that are not being hidden) */\r\n\tprivate Collection<String> shownCategories_ = new TreeSet<String>(new CategoryComparator());\r\n\r\n\t/** The set of category source models */\r\n\tCollection<CategorySource> sources = new ArrayList<CategorySource>();\r\n\r\n\t/**\r\n\t * Add all categories from a collection to the cache\r\n\t * \r\n\t * @param cats the categories\r\n\t */\r\n\tprivate void addAll(Collection<String> cats) {\r\n\t\tcategories_.addAll(cats);\r\n\t\tshownCategories_.addAll(cats);\r\n\t}\r\n\r\n\t/**\r\n\t * Add a category to the cache.\r\n\t * \r\n\t * @param cat the categories\r\n\t */\r\n\tpublic void addCategory(String cat) {\r\n\t\tcategories_.add(cat);\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Add a category source.\r\n\t * \r\n\t * @param s the source\r\n\t */\r\n\tpublic void addSource(CategorySource s) {\r\n\t\tsources.add(s);\r\n\t\taddAll(s.getCategories());\r\n\t}\r\n\r\n\t/**\r\n\t * Get all categories.\r\n\t * \r\n\t * @return the categories\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<String> getCategories() throws Exception {\r\n\t\tArrayList<String> cats = new ArrayList<String>();\r\n\t\tcats.addAll(categories_);\r\n\t\treturn (cats);\r\n\t}\r\n\r\n\t/**\r\n\t * Get the shown categories.\r\n\t * \r\n\t * @return the shown categories\r\n\t */\r\n\tpublic Collection<String> getShownCategories() {\r\n\t\tArrayList<String> cats = new ArrayList<String>();\r\n\t\tcats.addAll(shownCategories_);\r\n\t\treturn (cats);\r\n\t}\r\n\r\n\t/**\r\n\t * Checks if a category is being shown.\r\n\t * \r\n\t * @param cat the cat\r\n\t * \r\n\t * @return true, if is shown\r\n\t */\r\n\tpublic boolean isShown(String cat) {\r\n\t\treturn (shownCategories_.contains((cat == null || cat.equals(\"\")) ? CategoryModel.UNCATEGORIZED : cat)) ;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.Model#remove()\r\n\t */\r\n\t@Override\r\n\tpublic void remove() {\r\n\t  // empty\r\n\t}\r\n\t\r\n\t/**\r\n\t * delete a category from the model, but not from the db\r\n\t * @param cat\r\n\t */\r\n\tpublic void deleteCategory(String cat)\r\n\t{\r\n\t\tcategories_.remove(cat);\r\n\t\tshownCategories_.remove(cat);\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the set of shown categories.\r\n\t * \r\n\t * @param cats the shown categories\r\n\t */\r\n\tpublic void setShownCategories(Collection<String> cats) {\r\n\t\tshownCategories_ = cats;\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Show all categories.\r\n\t */\r\n\tpublic void showAll() {\r\n\t\tshownCategories_.clear();\r\n\t\tshownCategories_.addAll(categories_);\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Show a particular category.\r\n\t * \r\n\t * @param cat the category to show\r\n\t */\r\n\tpublic void showCategory(String cat) {\r\n\t\tshownCategories_.add(cat);\r\n\t}\r\n\r\n\t/**\r\n\t * Sync categories with the sources (clears the cache and re-reads the list of categories).\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\t@Override\r\n\tpublic void sync() {\r\n\t\t\r\n\t\tCollection<String> oldCategories = new TreeSet<String>(new CategoryComparator());\r\n\t\toldCategories.addAll(categories_);\r\n\t\tcategories_.clear();\r\n\t\tfor( CategorySource s : sources )\r\n\t\t{\r\n\t\t\tcategories_.addAll(s.getCategories());\t\r\n\t\t}\r\n\t\t\r\n\t\t// show any newly discovered categories\r\n\t\tfor( String s : categories_)\r\n\t\t{\r\n\t\t\tif( !oldCategories.contains(s))\r\n\t\t\t{\r\n\t\t\t\tthis.showCategory(s);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tthis.refreshListeners();\r\n\t\t\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\t\t// nothing to export\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\t\t// nothing to import\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"Categories\") + \":\" + getCategories().size();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/CheckListModel.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003-2010 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.db.CheckListDB;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.entity.CheckList;\r\nimport net.sf.borg.model.undo.CheckListUndoItem;\r\nimport net.sf.borg.model.undo.UndoLog;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Writer;\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The CheckList Model manages the CheckList Entities. CheckLists are keyed by a name. CheckLists contain simple text and\r\n * have stayed simple to be able to sync with the simple checkList functionality of a Palm Pilot.\r\n */\r\npublic class CheckListModel extends Model  {\r\n\r\n\t/**\r\n\t * class XmlContainer is solely for JAXB XML export/import\r\n\t * to keep the same XML structure as before JAXB was used\r\n\t */\r\n\t@XmlRootElement(name=\"CHECKLISTS\")\r\n\tprivate static class XmlContainer {\t\t\r\n\t\tpublic Collection<CheckList> CheckList;\t\t\r\n\t}\r\n\t\r\n\t/** The db */\r\n\tprivate final CheckListDB db_; // the database\r\n\r\n\t/** The singleton */\r\n\tstatic private final CheckListModel self_ = new CheckListModel();\r\n\r\n\t/**\r\n\t * Gets the singleton.\r\n\t * \r\n\t * @return the singleton\r\n\t */\r\n\tpublic static CheckListModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Gets the dB.\r\n\t * \r\n\t * @return the dB\r\n\t */\r\n\tpublic CheckListDB getDB() {\r\n\t\treturn db_;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all checkLists.\r\n\t * \r\n\t * @return all checkLists\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<CheckList> getCheckLists() throws Exception {\r\n\t\treturn db_.readAll();\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all checkList names.\r\n\t * \r\n\t * @return the checkList names\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<String> getNames() throws Exception {\r\n\r\n\t\treturn db_.getNames();\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new checkList model.\r\n\t */\r\n\tprivate CheckListModel() {\r\n\t\tdb_ = DBHelper.getFactory().createCheckListDB();\t\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a checkList by name\r\n\t * \r\n\t * @param name the checkList name\r\n\t * @param undo true if we are executing an undo\r\n\t */\r\n\tpublic void delete(String name, boolean undo) {\r\n\r\n\t\ttry {\r\n\t\t\tCheckList m = getCheckList(name);\r\n\t\t\t\r\n\t\t\tLinkModel.getReference().deleteLinksToEntity(m);\r\n\r\n\t\t\tif (m == null)\r\n\t\t\t\treturn;\r\n\t\t\tCheckList orig = m.clone();\r\n\r\n\t\t\tdb_.delete(m.getCheckListName());\r\n\t\t\tif (!undo) {\r\n\t\t\t\tUndoLog.getReference().addItem(CheckListUndoItem.recordDelete(orig));\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Save a checkList.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t */\r\n\tpublic void saveCheckList(CheckList checkList) {\r\n\t\tsaveCheckList(checkList, false);\r\n\t}\r\n\r\n\t/**\r\n\t * Save a checkList.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t * @param undo true if we are executing an undo\r\n\t */\r\n\tpublic void saveCheckList(CheckList checkList, boolean undo) {\r\n\r\n\t\ttry {\r\n\r\n\t\t\tString name = checkList.getCheckListName();\r\n\t\t\tCheckList old = db_.readCheckList(name);\r\n\t\t\tif (old == null) {\r\n\t\t\t\tdb_.addCheckList(checkList);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference()\r\n\t\t\t\t\t\t\t.addItem(CheckListUndoItem.recordAdd(checkList));\r\n\t\t\t\t}\r\n\r\n\t\t\t} else {\r\n\t\t\t\tdb_.updateCheckList(checkList);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference().addItem(\r\n\t\t\t\t\t\t\tCheckListUndoItem.recordUpdate(old));\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\t// inform views of data change\r\n\t\trefresh();\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Gets a checkList by name.\r\n\t * \r\n\t * @param name the checkList name\r\n\t * \r\n\t * @return the checkList\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic CheckList getCheckList(String name) throws Exception {\r\n\t\treturn db_.readCheckList(name);\r\n\t}\r\n\r\n\t/**\r\n\t * Export to XML\r\n\t * \r\n\t * @param fw the writer to write XML to\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n        Marshaller m = jc.createMarshaller();\r\n        XmlContainer container = new XmlContainer();\r\n        container.CheckList = getCheckLists();\r\n        m.marshal(container, fw);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Import xml.\r\n\t * \r\n\t * @param is the input stream containing the XML\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\t\r\n\t\tXmlContainer container =\r\n\t\t\t  (XmlContainer)u.unmarshal(\r\n\t\t\t    is );\r\n\t\t\r\n\t\tif( container.CheckList == null ) return;\r\n\r\n\t\tfor (CheckList checkList : container.CheckList ) {\r\n\t\t\tsaveCheckList(checkList,true);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Refresh listeners\r\n\t */\r\n\tpublic void refresh() {\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn \"CHECKLISTS\";\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"CheckLists\") + \": \"\r\n\t\t+ getCheckLists().size();\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/Day.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * Day.java\r\n *\r\n * Created on January 1, 2004, 10:19 PM\r\n */\r\n\r\npackage net.sf.borg.model;\r\n\r\nimport net.sf.borg.common.DateUtil;\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.entity.Appointment;\r\nimport net.sf.borg.model.entity.CalendarEntity;\r\nimport net.sf.borg.model.entity.LabelEntity;\r\n\r\nimport java.io.Serializable;\r\nimport java.util.*;\r\n\r\n\r\n/**\r\n * Class Day pulls together and manages all of the items that make up the\r\n * CalendarEntities for a single day. It packages together all of a day's info\r\n * as needed by a client (i.e. the UI).\r\n * \r\n */\r\npublic class Day {\r\n\r\n\t/**\r\n\t * class to compare appointment strings for sorting.\r\n\t */\r\n\tprivate static class apcompare implements Comparator<CalendarEntity>, Serializable {\r\n\r\n\t\t\r\n\t\tprivate static final long serialVersionUID = 1L;\r\n\r\n\t\t/*\r\n\t\t * (non-Javadoc)\r\n\t\t * \r\n\t\t * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)\r\n\t\t */\r\n\t\t@Override\r\n\t\tpublic int compare(CalendarEntity so1, CalendarEntity so2) {\r\n\r\n\t\t\tString s1 = so1.getText();\r\n\t\t\tString s2 = so2.getText();\r\n\r\n\t\t\tString psort = Prefs.getPref(PrefName.PRIORITY_SORT);\r\n\t\t\tif (psort.equals(\"true\")) {\r\n\t\t\t\tInteger p1 = so1.getPriority();\r\n\t\t\t\tInteger p2 = so2.getPriority();\r\n\t\t\t\t\r\n\t\t\t\tif( p1 != null && p2 != null )\r\n\t\t\t\t{\r\n\t\t\t\t\tif( p1.intValue() != p2.intValue())\r\n\t\t\t\t\t\treturn (p1.intValue() > p2.intValue() ? 1 : -1);\r\n\t\t\t\t}\r\n\t\t\t\telse if (p1 != null)\r\n\t\t\t\t\treturn -1;\r\n\t\t\t\telse if (p2 != null)\r\n\t\t\t\t\treturn 1;\r\n\t\t\t}\r\n\r\n\t\t\t// use appt time of day (not date - due to repeats) to sort next\r\n\t\t\t// appts with a time come before notes\r\n\t\t\tDate dt1 = null;\r\n\t\t\tDate dt2 = null;\r\n\t\t\tif (so1 instanceof Appointment\r\n\t\t\t\t\t&& !AppointmentModel.isNote((Appointment) so1)) {\r\n\t\t\t\tCalendar cal = new GregorianCalendar();\r\n\t\t\t\tcal.setTime(so1.getDate());\r\n\t\t\t\tcal.set(1, 1, 2000);\r\n\t\t\t\tdt1 = cal.getTime();\r\n\t\t\t}\r\n\t\t\tif (so2 instanceof Appointment\r\n\t\t\t\t\t&& !AppointmentModel.isNote((Appointment) so2)) {\r\n\t\t\t\tCalendar cal = new GregorianCalendar();\r\n\t\t\t\tcal.setTime(so2.getDate());\r\n\t\t\t\tcal.set(1, 1, 2000);\r\n\t\t\t\tdt2 = cal.getTime();\r\n\t\t\t}\r\n\r\n\t\t\tif (dt1 != null && dt2 != null)\r\n\t\t\t\treturn (dt1.after(dt2) ? 1 : -1);\r\n\t\t\tif (dt1 != null)\r\n\t\t\t\treturn -1;\r\n\t\t\tif (dt2 != null)\r\n\t\t\t\treturn 1;\r\n\r\n\t\t\t// if we got here, just compare\r\n\t\t\t// strings lexicographically\r\n\t\t\tint res = s1.compareTo(s2);\r\n\t\t\tif (res != 0)\r\n\t\t\t\treturn (res);\r\n\t\t\treturn (1);\r\n\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Adds appointments to the to day.\r\n\t * \r\n\t * @param day\r\n\t *            the day\r\n\t * @param l\r\n\t *            list of appointment keys to add\r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tprivate static void addToDay(Day day, Collection<Integer> l) throws Exception {\r\n\r\n\t\tboolean pub = true;\r\n\t\tboolean priv = false;\r\n\t\t//String sp = Prefs.getPref(PrefName.SHOWPUBLIC);\r\n\t\t//if (sp.equals(\"true\"))\r\n\t\t\t//pub = true;\r\n\t\tString sp = Prefs.getPref(PrefName.SHOWPRIVATE);\r\n\t\tif (sp.equals(\"true\"))\r\n\t\t\tpriv = true;\r\n\r\n\t\tif (l != null) {\r\n\t\t\tIterator<Integer> it = l.iterator();\r\n\t\t\tAppointment appt;\r\n\r\n\t\t\t// iterate through the day's appts\r\n\t\t\twhile (it.hasNext()) {\r\n\t\t\t\tInteger ik = it.next();\r\n\r\n\t\t\t\t// read the appt from the DB\r\n\t\t\t\tappt = AppointmentModel.getReference().getAppt(ik.intValue());\r\n\r\n\t\t\t\t// skip based on public/private flags\r\n\t\t\t\tif (appt.isPrivate()) {\r\n\t\t\t\t\tif (!priv)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tif (!pub)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t// only show current todo\r\n\t\t\t\tif( Prefs.getBoolPref(PrefName.TODO_ONLY_SHOW_CURRENT) ) {\r\n\t\t\t\t\tif( appt.isTodo() && appt.getNextTodo() != null && DateUtil.dayOfEpoch(day.cal.getTime()) > DateUtil.dayOfEpoch(appt.getNextTodo())) {\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif( appt.isTodo() && appt.getNextTodo() == null && Repeat.isRepeating(appt) && DateUtil.dayOfEpoch(day.cal.getTime()) != DateUtil.dayOfEpoch(appt.getDate())) {\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tString color = appt.getColor();\r\n\t\t\t\tif (color == null)\r\n\t\t\t\t\tappt.setColor(Theme.COLOR4);\r\n\r\n\t\t\t\t// add apptto day\r\n\t\t\t\tday.addItem(appt);\r\n\r\n\t\t\t\t// set vacation and holiday flags at dayinfo level\r\n\t\t\t\tInteger v = appt.getVacation();\r\n\t\t\t\tif (v != null && v.intValue() != 0)\r\n\t\t\t\t\tday.setVacation(v.intValue());\r\n\r\n\t\t\t\tv = appt.getHoliday();\r\n\t\t\t\tif (v != null && v.intValue() == 1)\r\n\t\t\t\t\tday.setHoliday(1);\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the Day information for a given day.\r\n\t * \r\n\t * @param year\r\n\t *            the year\r\n\t * @param month\r\n\t *            the month\r\n\t * @param day\r\n\t *            the day\r\n\t * @return the Day object\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic static Day getDay(int year, int month, int day) throws Exception {\r\n\r\n\t\t// get the base day key\r\n\t\tCalendar cal = new GregorianCalendar(year, month, day);\r\n\r\n\t\tDay ret = new Day(cal);\r\n\r\n\t\t// get the list of appt keys from the map_\r\n\t\tCollection<Integer> l = AppointmentModel.getReference().getAppts(\r\n\t\t\t\tcal.getTime());\r\n\t\taddToDay(ret, l);\r\n\r\n\t\t// daylight savings time\r\n\t\tGregorianCalendar gc = new GregorianCalendar(year, month, day, 11, 00);\r\n\t\tboolean dstNow = TimeZone.getDefault().inDaylightTime(gc.getTime());\r\n\t\tgc.add(Calendar.DATE, -1);\r\n\t\tboolean dstYesterday = TimeZone.getDefault().inDaylightTime(\r\n\t\t\t\tgc.getTime());\r\n\t\tif (dstNow && !dstYesterday) {\r\n\t\t\tLabelEntity hol = new LabelEntity();\r\n\t\t\thol.setColor(Theme.COLOR4);\r\n\t\t\thol.setText(Resource.getResourceString(\"Daylight_Savings_Time\"));\r\n\t\t\tret.addItem(hol);\r\n\t\t} else if (!dstNow && dstYesterday) {\r\n\t\t\tLabelEntity hol = new LabelEntity();\r\n\t\t\thol.setColor(Theme.COLOR4);\r\n\t\t\thol.setText(Resource.getResourceString(\"Standard_Time\"));\r\n\t\t\tret.addItem(hol);\r\n\t\t}\r\n\r\n\t\t// add canned US holidays\r\n\t\t// check user preferences first\r\n\t\tString show_us_hols = Prefs.getPref(PrefName.SHOWUSHOLIDAYS);\r\n\r\n\t\tif (show_us_hols.equals(\"true\")) {\r\n\r\n\t\t\t// ok, we will add holiday appts\r\n\t\t\t// to the dayinfo for the US holidays below\r\n\t\t\t// the dayinfo.holiday flag is set if the holiday\r\n\t\t\t// is a day off from work and should cause the day to have\r\n\t\t\t// holiday coloring on the gui\r\n\t\t\t//\r\n\t\t\t// so, holidays only exist in the dayinfo objects, which are\r\n\t\t\t// temporary. they do not get added to the DB or even the appt\r\n\t\t\t// map_\r\n\t\t\tLabelEntity hol = new LabelEntity();\r\n\r\n\t\t\thol.setDate(new GregorianCalendar(year, month, day, 00, 00)\r\n\t\t\t\t\t.getTime());\r\n\r\n\t\t\thol.setColor(Theme.HOLIDAYCOLOR);\r\n\r\n\t\t\thol.setText(null);\r\n\t\t\tif (month == 9 && day == 31) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Halloween\"));\r\n\t\t\t} else if (month == 6 && day == 4) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Independence_Day\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t} else if (month == 1 && day == 2) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Ground_Hog_Day\"));\r\n\t\t\t} else if (month == 1 && day == 14) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Valentine's_Day\"));\r\n\t\t\t} else if (month == 2 && day == 17) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"St._Patrick's_Day\"));\r\n\t\t\t} else if (month == 10 && day == 11) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Veteran's_Day\"));\r\n\t\t\t} else if (month == 8\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 1)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Labor_Day\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t} else if (month == 0\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 3)) {\r\n\t\t\t\thol.setText(Resource\r\n\t\t\t\t\t\t.getResourceString(\"Martin_Luther_King_Day\"));\r\n\t\t\t} else if (month == 1\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 3)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Presidents_Day\"));\r\n\t\t\t} else if (month == 4\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, -1)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Memorial_Day\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t} else if (month == 9\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 2)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Columbus_Day\"));\r\n\t\t\t} else if (month == 4\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.SUNDAY, 2)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Mother's_Day\"));\r\n\t\t\t} else if (month == 5\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.SUNDAY, 3)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Father's_Day\"));\r\n\t\t\t} else if (month == 10\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.THURSDAY, 4)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Thanksgiving\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t}\r\n\r\n\t\t\tif (hol.getText() != null)\r\n\t\t\t\tret.addItem(hol);\r\n\r\n\t\t}\r\n\r\n\t\t// add canned Canadian holidays\r\n\t\t// check user preferences first\r\n\t\tString show_can_hols = Prefs.getPref(PrefName.SHOWCANHOLIDAYS);\r\n\t\tif (show_can_hols.equals(\"true\")) {\r\n\r\n\t\t\tLabelEntity hol = new LabelEntity();\r\n\t\t\thol.setDate(new GregorianCalendar(year, month, day, 00, 00)\r\n\t\t\t\t\t.getTime());\r\n\r\n\t\t\thol.setColor(Theme.HOLIDAYCOLOR);\r\n\r\n\t\t\thol.setText(null);\r\n\t\t\tif (month == 6 && day == 1) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Canada_Day\"));\r\n\t\t\t} else if (month == 11 && day == 26) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Boxing_Day\"));\r\n\t\t\t} else if (month == 7\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 1)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Civic_Holiday\"));\r\n\t\t\t} else if (month == 10 && day == 11) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Remembrance_Day\"));\r\n\t\t\t} else if (month == 8\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 1)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Labour_Day_(Can)\"));\r\n\t\t\t} else if (month == 2\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 2)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Commonwealth_Day\"));\r\n\t\t\t} else if (month == 9\r\n\t\t\t\t\t&& day == nthdom(year, month, Calendar.MONDAY, 2)) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Thanksgiving_(Can)\"));\r\n\t\t\t} else if (month == 4) {\r\n\t\t\t\tgc = new GregorianCalendar(year, month, 25);\r\n\t\t\t\tint diff = gc.get(Calendar.DAY_OF_WEEK);\r\n\t\t\t\tdiff += 5;\r\n\t\t\t\tif (diff > 7)\r\n\t\t\t\t\tdiff -= 7;\r\n\t\t\t\tif (day == 25 - diff) {\r\n\t\t\t\t\thol.setText(Resource.getResourceString(\"Victoria_Day\"));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (hol.getText() != null)\r\n\t\t\t\tret.addItem(hol);\r\n\r\n\t\t}\r\n\t\t\r\n\t\t// common holidays\r\n\t\tif (show_can_hols.equals(\"true\") || show_us_hols.equals(\"true\")) {\r\n\r\n\t\t\tLabelEntity hol = new LabelEntity();\r\n\t\t\thol.setDate(new GregorianCalendar(year, month, day, 00, 00)\r\n\t\t\t\t\t.getTime());\r\n\r\n\t\t\thol.setColor(Theme.HOLIDAYCOLOR);\r\n\r\n\t\t\thol.setText(null);\r\n\t\t\tif (month == 0 && day == 1) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"New_Year's_Day\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t} else if (month == 11 && day == 25) {\r\n\t\t\t\thol.setText(Resource.getResourceString(\"Christmas\"));\r\n\t\t\t\tret.setHoliday(1);\r\n\t\t\t}\r\n\r\n\t\t\tif (hol.getText() != null)\r\n\t\t\t\tret.addItem(hol);\r\n\r\n\t\t}\r\n\r\n\r\n\t\tfor( Model m : Model.getExistingModels())\r\n\t\t{\r\n\t\t\tif( m instanceof CalendarEntityProvider)\r\n\t\t\t{\r\n\t\t\t\tList<CalendarEntity> el  = ((CalendarEntityProvider) m).getEntities(cal.getTime());\r\n\t\t\t\tfor( CalendarEntity e : el )\r\n\t\t\t\t\tret.addItem(e);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\r\n\t\treturn (ret);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * compute nth day of month for calculating when certain holidays fall.\r\n\t * \r\n\t * @param year\r\n\t *            the year\r\n\t * @param month\r\n\t *            the month\r\n\t * @param dayofweek\r\n\t *            the day of the week\r\n\t * @param week\r\n\t *            the week of the month\r\n\t * \r\n\t * @return the date\r\n\t */\r\n\tprivate static int nthdom(int year, int month, int dayofweek, int week) {\r\n\t\tGregorianCalendar cal = new GregorianCalendar(year, month, 1);\r\n\t\tcal.set(Calendar.DAY_OF_WEEK, dayofweek);\r\n\t\tcal.set(Calendar.DAY_OF_WEEK_IN_MONTH, week);\r\n\t\treturn (cal.get(Calendar.DATE));\r\n\t}\r\n\r\n\tprivate int holiday; // set to indicate if any appt in the list is a\r\n\r\n\tprivate final TreeSet<CalendarEntity> items; // list of appts for the day\r\n\r\n\tprivate int vacation; // vacation value for the day\r\n\t\r\n\tprivate final Calendar cal;\r\n\r\n\t/**\r\n\t * Instantiates a new day.\r\n\t */\r\n\tprivate Day(Calendar cal) {\r\n\r\n\t\tholiday = 0;\r\n\t\tvacation = 0;\r\n\t\titems = new TreeSet<CalendarEntity>(new apcompare());\r\n\t\tthis.cal = cal;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Adds a CalendarEntity item to the Day\r\n\t * \r\n\t * @param info\r\n\t *            the CalendarEntity\r\n\t */\r\n\tprivate void addItem(CalendarEntity info) {\r\n\t\titems.add(info);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the holiday flag.\r\n\t * \r\n\t * @return the holiday (1 = holiday)\r\n\t */\r\n\tpublic int getHoliday() {\r\n\t\treturn (holiday);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all CalendarEntity items for the Day.\r\n\t * \r\n\t * @return the items\r\n\t */\r\n\tpublic Collection<CalendarEntity> getItems() {\r\n\t\treturn (items);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the vacation value for the Day.\r\n\t * \r\n\t * @return the vacation value (0 = none, 1 = full day, 2 = half day)\r\n\t */\r\n\tpublic int getVacation() {\r\n\t\treturn (vacation);\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the holiday value\r\n\t * \r\n\t * @param i\r\n\t *            the new holiday value\r\n\t */\r\n\tpublic void setHoliday(int i) {\r\n\t\tholiday = i;\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the vacation value\r\n\t * \r\n\t * @param i\r\n\t *            the new vacation value\r\n\t */\r\n\tpublic void setVacation(int i) {\r\n\t\tvacation = i;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/EmailReminder.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * popups.java\r\n *\r\n * Created on January 16, 2004, 3:08 PM\r\n */\r\n\r\npackage net.sf.borg.model;\r\n\r\nimport java.text.DateFormat;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Calendar;\r\nimport java.util.Collection;\r\nimport java.util.Date;\r\nimport java.util.GregorianCalendar;\r\nimport java.util.StringTokenizer;\r\nimport java.util.logging.Logger;\r\n\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.common.SendJavaMail;\r\nimport net.sf.borg.model.entity.Appointment;\r\nimport net.sf.borg.model.entity.CalendarEntity;\r\nimport net.sf.borg.model.entity.Subtask;\r\nimport net.sf.borg.model.entity.Task;\r\n\r\n/**\r\n * this class handles the daily email reminder\r\n */\r\n\r\npublic class EmailReminder {\r\n\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t/**\r\n\t * Checks if entity should be shown as strike-through on a certain date.\r\n\t * \r\n\t * @param appt the entity\r\n\t * @param date the date\r\n\t * \r\n\t * @return true, if is strike\r\n\t */\r\n\tprivate static boolean isStrike(CalendarEntity appt, Date date) {\r\n\t\treturn (appt.getColor() != null && appt.getColor().equals(\"strike\"))\r\n\t\t\t\t|| (appt.isTodo() && !(appt.getNextTodo() == null || !appt.getNextTodo().after(date)));\r\n\t}\r\n\r\n\tstatic public boolean needToSendDailyEmail() {\r\n\t\t// check if the email feature has been enabled\r\n\t\tString email = Prefs.getPref(PrefName.EMAILENABLED);\r\n\t\tif (email.equals(\"false\"))\r\n\t\t\treturn false;\r\n\r\n\t\t// get the SMTP host and address\r\n\t\tString host = Prefs.getPref(PrefName.EMAILSERVER);\r\n\t\tString addr = Prefs.getPref(PrefName.EMAILADDR);\r\n\r\n\t\tif (host.equals(\"\") || addr.equals(\"\"))\r\n\t\t\treturn false;\r\n\r\n\t\tCalendar cal = new GregorianCalendar();\r\n\r\n\t\tint doy = -1;\r\n\r\n\t\t// get the last day that email was sent\r\n\t\tint lastday = Prefs.getIntPref(PrefName.EMAILLAST);\r\n\r\n\t\t// if email was already sent today - don't send again\r\n\t\tdoy = cal.get(Calendar.DAY_OF_YEAR);\r\n\t\tif (doy == lastday)\r\n\t\t\treturn false;\r\n\r\n\t\treturn true;\r\n\r\n\t}\r\n\r\n\tstatic public void sendDailyEmailReminder(Calendar emailday, String passwd) throws Exception {\r\n\t\tsendDailyEmailReminder(emailday, false, passwd);\r\n\t}\r\n\r\n\t/**\r\n\t * Send daily email reminder.\r\n\t * \r\n\t * @param emailday    the emailday\r\n\t * \r\n\t * @param forceResend resend the daily email, even if it was already sent\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tstatic public void sendDailyEmailReminder(Calendar emailday, boolean forceResend, String passwd) throws Exception {\r\n\r\n\t\t// check if the email feature has been enabled\r\n\t\tString email = Prefs.getPref(PrefName.EMAILENABLED);\r\n\t\tif (email.equals(\"false\"))\r\n\t\t\treturn;\r\n\r\n\t\t// get the SMTP host and address\r\n\t\tString host = Prefs.getPref(PrefName.EMAILSERVER);\r\n\t\tString addr = Prefs.getPref(PrefName.EMAILADDR);\r\n\t\tString from = Prefs.getPref(PrefName.EMAILFROM);\r\n\r\n\t\tif (host.equals(\"\") || addr.equals(\"\"))\r\n\t\t\treturn;\r\n\r\n\t\tCalendar cal = new GregorianCalendar();\r\n\r\n\t\t// if no date passed in, the timer has gone off and we need to check if\r\n\t\t// we\r\n\t\t// can send\r\n\t\t// email now\r\n\t\tint doy = -1;\r\n\t\tif (emailday == null) {\r\n\t\t\t// get the last day that email was sent\r\n\t\t\tint lastday = Prefs.getIntPref(PrefName.EMAILLAST);\r\n\r\n\t\t\t// if email was already sent today - don't send again\r\n\t\t\tdoy = cal.get(Calendar.DAY_OF_YEAR);\r\n\t\t\tif (doy == lastday && forceResend == false)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t// create the calendar model key for tomorrow\r\n\t\t\tcal.add(Calendar.DATE, 1);\r\n\t\t} else {\r\n\t\t\t// just send email for the requested day\r\n\t\t\tcal = emailday;\r\n\t\t}\r\n\r\n\t\t// tx is the contents of the email\r\n\t\tString ap_tx = \"Appointments for \" + DateFormat.getDateInstance().format(cal.getTime()) + \":\\n\";\r\n\t\tStringBuffer tx = new StringBuffer();\r\n\r\n\t\t// get the list of appts for the requested day\r\n\t\tCollection<Integer> l = AppointmentModel.getReference().getAppts(cal.getTime());\r\n\t\tif (l != null) {\r\n\r\n\t\t\tAppointment appt;\r\n\r\n\t\t\t// iterate through the day's appts\r\n\t\t\tfor (Integer ik : l) {\r\n\r\n\t\t\t\ttry {\r\n\t\t\t\t\t// read the appointment from the calendar model\r\n\t\t\t\t\tappt = AppointmentModel.getReference().getAppt(ik.intValue());\r\n\r\n\t\t\t\t\t// get the appt flags to see if the appointment is private\r\n\t\t\t\t\t// if so, don't include it in the email\r\n\t\t\t\t\tif (appt.isPrivate())\r\n\t\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t\t// skip strike through items\r\n\t\t\t\t\tif (isStrike(appt, cal.getTime()))\r\n\t\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t\tif (!AppointmentModel.isNote(appt)) {\r\n\t\t\t\t\t\t// add the appointment time to the email if it is not a\r\n\t\t\t\t\t\t// note\r\n\t\t\t\t\t\tDate d = appt.getDate();\r\n\t\t\t\t\t\tSimpleDateFormat df = AppointmentModel.getTimeFormat();\r\n\t\t\t\t\t\ttx.append(df.format(d) + \" \");\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t// add the appointment text\r\n\t\t\t\t\tif (appt.isEncrypted())\r\n\t\t\t\t\t\ttx.append(Resource.getResourceString(\"EncryptedItemShort\"));\r\n\t\t\t\t\telse {\r\n\t\t\t\t\t\t// only show first line of appointment text\r\n\t\t\t\t\t\tString s = appt.getText();\r\n\t\t\t\t\t\tint ii = s.indexOf('\\n');\r\n\t\t\t\t\t\tif (ii != -1) {\r\n\t\t\t\t\t\t\ttx.append(s, 0, ii);\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\ttx.append(s);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttx.append(\"\\n\");\r\n\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\tlog.severe(e.toString());\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\t// load any task tracker items for the email\r\n\t\tCollection<Task> tasks = TaskModel.getReference().get_tasks(cal.getTime());\r\n\t\tif (tasks != null) {\r\n\r\n\t\t\tfor (Task task : tasks) {\r\n\t\t\t\t// add each task to the email - and remove newlines\r\n\t\t\t\ttx.append(\"Task[\" + task.getKey() + \"] \");\r\n\t\t\t\ttx.append(task.getSummary());\r\n\t\t\t\ttx.append(\"\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tCollection<Subtask> subtasks = TaskModel.getReference().get_subtasks(cal.getTime());\r\n\t\tif (subtasks != null) {\r\n\r\n\t\t\tfor (Subtask subtask : subtasks) {\r\n\r\n\t\t\t\t// add each task to the email - and remove newlines\r\n\t\t\t\ttx.append(\"Subtask[\" + subtask.getKey() + \"] \");\r\n\t\t\t\ttx.append(subtask.getDescription());\r\n\t\t\t\ttx.append(\"\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// add any outstanding todos\r\n\t\tCollection<Appointment> todos = AppointmentModel.getReference().get_todos();\r\n\t\tStringBuffer tdbuf = new StringBuffer();\r\n\t\tfor (Appointment todo : todos) {\r\n\t\t\tDate nt = todo.getNextTodo();\r\n\t\t\tif (nt == null) {\r\n\t\t\t\tnt = todo.getDate();\r\n\t\t\t}\r\n\r\n\t\t\tCalendar tdcal = new GregorianCalendar();\r\n\t\t\ttdcal.setTime(nt);\r\n\t\t\ttdcal.set(Calendar.SECOND, 59);\r\n\t\t\ttdcal.set(Calendar.MINUTE, 59);\r\n\t\t\ttdcal.set(Calendar.HOUR_OF_DAY, 23);\r\n\r\n\t\t\tif (tdcal.before(cal)) {\r\n\t\t\t\tif (!AppointmentModel.isNote(todo)) {\r\n\t\t\t\t\t// add the appointment time to the email if it is not a\r\n\t\t\t\t\t// note\r\n\t\t\t\t\tDate d = todo.getDate();\r\n\t\t\t\t\tSimpleDateFormat df = AppointmentModel.getTimeFormat();\r\n\t\t\t\t\ttdbuf.append(df.format(d) + \" \");\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// add the appointment text\r\n\t\t\t\tif (todo.isEncrypted())\r\n\t\t\t\t\ttdbuf.append(Resource.getResourceString(\"EncryptedItemShort\"));\r\n\t\t\t\telse {\r\n\t\t\t\t\t// only show first line of appointment text\r\n\t\t\t\t\tString s = todo.getText();\r\n\t\t\t\t\tint ii = s.indexOf('\\n');\r\n\t\t\t\t\tif (ii != -1) {\r\n\t\t\t\t\t\ttdbuf.append(s, 0, ii);\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\ttdbuf.append(s);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\ttdbuf.append(\"\\n\");\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\ttasks = TaskModel.getReference().get_tasks();\r\n\t\tif (tasks != null) {\r\n\r\n\t\t\tfor (Task task : tasks) {\r\n\r\n\t\t\t\tif (TaskModel.isClosed(task))\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tDate d = task.getDueDate();\r\n\t\t\t\tif (d != null) {\r\n\t\t\t\t\tCalendar tdcal = new GregorianCalendar();\r\n\t\t\t\t\ttdcal.setTime(d);\r\n\t\t\t\t\ttdcal.set(Calendar.SECOND, 59);\r\n\t\t\t\t\ttdcal.set(Calendar.MINUTE, 59);\r\n\t\t\t\t\ttdcal.set(Calendar.HOUR_OF_DAY, 23);\r\n\r\n\t\t\t\t\tif (tdcal.before(cal)) {\r\n\t\t\t\t\t\ttdbuf.append(\"Task[\" + task.getKey() + \"] \");\r\n\t\t\t\t\t\ttdbuf.append(task.getSummary());\r\n\t\t\t\t\t\ttdbuf.append(\"\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tsubtasks = TaskModel.getReference().getSubTasks();\r\n\t\tif (subtasks != null) {\r\n\r\n\t\t\tfor (Subtask subtask : subtasks) {\r\n\r\n\t\t\t\tDate d = subtask.getDueDate();\r\n\t\t\t\tif (d != null && subtask.getCloseDate() == null) {\r\n\t\t\t\t\tCalendar tdcal = new GregorianCalendar();\r\n\t\t\t\t\ttdcal.setTime(d);\r\n\t\t\t\t\ttdcal.set(Calendar.SECOND, 59);\r\n\t\t\t\t\ttdcal.set(Calendar.MINUTE, 59);\r\n\t\t\t\t\ttdcal.set(Calendar.HOUR_OF_DAY, 23);\r\n\r\n\t\t\t\t\tif (tdcal.before(cal)) {\r\n\t\t\t\t\t\ttdbuf.append(\"Subtask[\" + subtask.getKey() + \"] \");\r\n\t\t\t\t\t\ttdbuf.append(subtask.getDescription());\r\n\t\t\t\t\t\ttdbuf.append(\"\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!tdbuf.toString().equals(\"\")) {\r\n\t\t\ttx.append(\"\\n\\n\");\r\n\t\t\ttx.append(Resource.getResourceString(\"OverDue\"));\r\n\t\t\ttx.append(\"\\n\" + tdbuf);\r\n\t\t}\r\n\r\n\t\t// send the email using SMTP\r\n\t\tif (!tx.toString().equals(\"\")) {\r\n\t\t\tString stx = ap_tx + tx;\r\n\t\t\tStringTokenizer stk = new StringTokenizer(addr, \",;\");\r\n\t\t\twhile (stk.hasMoreTokens()) {\r\n\t\t\t\tString a = stk.nextToken();\r\n\t\t\t\tString f;\r\n\t\t\t\tif (from == null || from.isEmpty())\r\n\t\t\t\t\tf = a.trim();\r\n\t\t\t\telse\r\n\t\t\t\t\tf = from;\r\n\t\t\t\tif (!a.equals(\"\")) {\r\n\r\n\t\t\t\t\tSendJavaMail.sendMail(host, stx, Resource.getResourceString(\"Reminder_Notice\"), f, a.trim(),\r\n\t\t\t\t\t\t\tPrefs.getPref(PrefName.EMAILUSER), passwd);\r\n\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlog.info(\"Skipping email\");\r\n\t\t}\r\n\t\t// record that we sent email today\r\n\t\tif (doy != -1)\r\n\t\t\tPrefs.putPref(PrefName.EMAILLAST, Integer.valueOf(doy));\r\n\r\n\t\treturn;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/ExportImport.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.model;\n\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * contains common import/export utilities\n * \n */\npublic class ExportImport {\n\n\t/**\n\t * Export all models to XML files inside a time-stamped zipfile.\n\t * \n\t * @param dir\n\t *            the directory to create the zip file in\n\t * @throws Exception\n\t */\n\tpublic static void exportToZip(String dir)\n\t\t\tthrows Exception {\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\n\t\tString uniq = sdf.format(new Date());\n\n\t\tString backupFilename = dir + \"/borg\" + uniq + \".zip\";\n\t\tZipOutputStream out = new ZipOutputStream(new FileOutputStream(\n\t\t\t\tbackupFilename));\n\t\tWriter fw = new OutputStreamWriter(out, StandardCharsets.UTF_8);\n\n\t\tfor (Model model : Model.getExistingModels()) {\n\t\t\t// links must be last\n\t\t\tif (model instanceof LinkModel) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\n\t\t\tif (model.getExportName() == null)\n\t\t\t\tcontinue;\n\n\t\t\tout.putNextEntry(new ZipEntry(model.getExportName() + \".xml\"));\n\t\t\tmodel.export(fw);\n\t\t\tfw.flush();\n\t\t\tout.closeEntry();\n\n\t\t}\n\n\t\t// links must be last for import to work\n\t\tout.putNextEntry(new ZipEntry(LinkModel.getReference().getExportName()\n\t\t\t\t+ \".xml\"));\n\t\tLinkModel.getReference().export(fw);\n\t\tfw.flush();\n\t\tout.closeEntry();\n\n\t\tout.close();\n\n\t\t\n\t}\n\n\t/**\n\t * Import from a single xml input stream.\n\t * \n\t * @param model\n\t *            the Model to use for the import\n\t * @param is\n\t *            the input stream\n\t * @throws Exception\n\t */\n\tpublic static void importFromXmlFile(Model model, InputStream is)\n\t\t\tthrows Exception {\n\n\t\tmodel.importXml(is);\n\n\t\t// show any newly imported categories\n\t\tCategoryModel.getReference().sync();\n\t\tCategoryModel.getReference().showAll();\n\t}\n\n\t/**\n\t * get the model that can import the given XML\n\t * \n\t * @param in\n\t *            - BufferedReader for the XML\n\t * @return the Model\n\t * @throws IOException\n\t */\n\tpublic static Model getImportModelForXML(BufferedReader in)\n\t\t\tthrows IOException {\n\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tString line = in.readLine();\n\t\t\tif (line != null) {\n\t\t\t\tfor (Model model : Model.getExistingModels()) {\n\n\t\t\t\t\tif (model.getExportName() != null\n\t\t\t\t\t\t\t&& line.contains(model.getExportName())) {\n\t\t\t\t\t\tin.close();\n\t\t\t\t\t\treturn model;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tin.close();\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * OMG - the JAXB unmarshaller keeps closing the entire Zip Input Stream\n\t * after unmarshalling a single entry. Need to define this horrible class to\n\t * prevent this. Actually it is pretty elegant compared to other options -\n\t * like using temp files or reading entire entries into memory to clone\n\t * streams\n\t * \n\t */\n\tprivate static class UncloseableZipInputStream extends ZipInputStream {\n\n\t\tpublic UncloseableZipInputStream(InputStream in) {\n\t\t\tsuper(in);\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\t// do nothing - prevent the stream from being closed\n\t\t}\n\n\t\tpublic void myClose() throws IOException {\n\t\t\tsuper.close();\n\t\t}\n\n\t}\n\n\n\t/**\n\t * Import an entire backup Zip file\n\t * \n\t * @param zipFileName\n\t *            the backup file name\n\t * @throws Exception\n\t */\n\tstatic public void importFromZip(String zipFileName) throws Exception {\n\t\tUncloseableZipInputStream in = new UncloseableZipInputStream(\n\t\t\t\tnew FileInputStream(zipFileName));\n\n\t\t// force loading of LinkModel in case the UI hasn't had reason to load\n\t\t// it\n\t\tLinkModel.getReference();\n\n\t\t// assumes that links are last entry\n\t\tfor (ZipEntry entry = in.getNextEntry(); entry != null; entry = in\n\t\t\t\t.getNextEntry()) {\n\n\n\t\t\tboolean import_done = false;\n\t\t\tfor (Model model : Model.getExistingModels()) {\n\t\t\t\tif (entry.getName().equals(model.getExportName() + \".xml\")) {\n\t\t\t\t\timportFromXmlFile(model, in);\n\t\t\t\t\timport_done = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (import_done == false) {\n\t\t\t\tthrow new Exception(\"Unknown file in ZIP - \" + entry.getName()\n\t\t\t\t\t\t+ \" ...skipping\");\n\t\t\t}\n\n\t\t}\n\n\t\t// really close the zip file\n\t\tin.myClose();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/LinkModel.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.EntityDB;\r\nimport net.sf.borg.model.db.LinkDB;\r\nimport net.sf.borg.model.entity.*;\r\n\r\nimport java.io.*;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.HashMap;\r\nimport java.util.Iterator;\r\n\r\n/**\r\n * LinkModel manages the Link Entities, which are associations between BORG\r\n * Entities and other BORG Entities, files, and URLs.\r\n */\r\npublic class LinkModel extends Model {\r\n\r\n\t/**\r\n\t * class XmlContainer is solely for JAXB XML export/import to keep the same\r\n\t * XML structure as before JAXB was used\r\n\t */\r\n\t@XmlRootElement(name = \"LINKS\")\r\n\tprivate static class XmlContainer {\r\n\t\tpublic Collection<Link> Link;\r\n\t}\r\n\r\n\t/**\r\n\t * LinkType holds the various link types. The string values are for legacy\r\n\t * reasons\r\n\t */\r\n\tpublic enum LinkType {\r\n\r\n\t\tFILELINK(\"file\"), ATTACHMENT(\"attachment\"), URL(\"url\"), APPOINTMENT(\r\n\t\t\t\t\"appointment\"), MEMO(\"memo\"), PROJECT(\"project\"), TASK(\"task\"), ADDRESS(\r\n\t\t\t\t\"address\"), CHECKLIST(\"net.sf.borg.ui.checklist\");\r\n\r\n\t\tprivate final String value;\r\n\r\n\t\tLinkType(String n) {\r\n\t\t\tvalue = n;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic String toString() {\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/** The singleton */\r\n\tstatic private final LinkModel self_ = new LinkModel();\r\n\r\n\t/** map of entity types to class names */\r\n\tprivate static final HashMap<Class<?>, LinkType> typemap = new HashMap<Class<?>, LinkType>();\r\n\r\n\tstatic {\r\n\t\t// owner types\r\n\t\ttypemap.put(Appointment.class, LinkType.APPOINTMENT);\r\n\t\ttypemap.put(Memo.class, LinkType.MEMO);\r\n\t\ttypemap.put(Task.class, LinkType.TASK);\r\n\t\ttypemap.put(Address.class, LinkType.ADDRESS);\r\n\t\ttypemap.put(Project.class, LinkType.PROJECT);\r\n\t\ttypemap.put(CheckList.class, LinkType.CHECKLIST);\r\n\t}\r\n\r\n\t/**\r\n\t * get the folder where attachments are stored\r\n\t * \r\n\t * @return the attachment folder path\r\n\t */\r\n\tpublic static String attachmentFolder() {\r\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\r\n\t\t\r\n\t\tif (dbtype.equals(\"h2\")) {\r\n\t\t\tString path = Prefs.getPref(PrefName.H2DIR) + \"/attachments\";\r\n\t\t\tFile f = new File(path);\r\n\t\t\tif (!f.exists()) {\r\n\t\t\t\tif (!f.mkdir()) {\r\n\t\t\t\t\tErrmsg.getErrorHandler()\r\n\t\t\t\t\t\t\t.notice(Resource\r\n\t\t\t\t\t\t\t\t\t.getResourceString(\"att_folder_err\") + path);\r\n\t\t\t\t\treturn null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn path;\r\n\t\t}\r\n\t\tif (dbtype.equals(\"sqlite\")) {\r\n\t\t\tString path = Prefs.getPref(PrefName.SQLITEDIR) + \"/attachments\";\r\n\t\t\tFile f = new File(path);\r\n\t\t\tif (!f.exists()) {\r\n\t\t\t\tif (!f.mkdir()) {\r\n\t\t\t\t\tErrmsg.getErrorHandler()\r\n\t\t\t\t\t\t\t.notice(Resource\r\n\t\t\t\t\t\t\t\t\t.getResourceString(\"att_folder_err\") + path);\r\n\t\t\t\t\treturn null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn path;\r\n\t\t}\r\n\t\tif(  dbtype.equals(\"jdbc\")) {\r\n\t\t\tString url  = Prefs.getPref(PrefName.JDBCURL);\r\n\t\t\tif( url.startsWith(\"jdbc:sqlite:\") && url.contains(\"/\")){\r\n\t\t\t\tString path = url.substring(12, url.lastIndexOf('/')) + \"/attachments\";\r\n\t\t\t\tFile f = new File(path);\r\n\t\t\t\tif (!f.exists()) {\r\n\t\t\t\t\tif (!f.mkdir()) {\r\n\t\t\t\t\t\tErrmsg.getErrorHandler()\r\n\t\t\t\t\t\t\t\t.notice(Resource\r\n\t\t\t\t\t\t\t\t\t\t.getResourceString(\"att_folder_err\") + path);\r\n\t\t\t\t\t\treturn null;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn path;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the singleton.\r\n\t * \r\n\t * @return the singleton\r\n\t */\r\n\tpublic static LinkModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\t/** The db */\r\n\tprivate final EntityDB<Link> db_; // the database\r\n\r\n\t/**\r\n\t * Adds a link.\r\n\t * \r\n\t * @param owner\r\n\t *            the owning Entity\r\n\t * @param pathIn\r\n\t *            the path (url, filepath, or entity key)\r\n\t * @param linkType\r\n\t *            the link type\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void addLink(KeyedEntity<?> owner, String pathIn, LinkType linkType)\r\n\t\t\tthrows Exception {\r\n\t\tString path = pathIn;\r\n\t\tif (owner == null) {\r\n\t\t\tErrmsg.getErrorHandler().notice(\r\n\t\t\t\t\tResource.getResourceString(\"att_owner_null\"));\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (linkType == LinkType.ATTACHMENT) {\r\n\r\n\t\t\tString atfolder = attachmentFolder();\r\n\t\t\tif (atfolder == null)\r\n\t\t\t\tthrow new Exception(\"attachments not supported\");\r\n\r\n\t\t\t// need to copy file and create new path\r\n\t\t\tFile orig = new File(path);\r\n\t\t\tString fname = orig.getName();\r\n\t\t\tString newpath = atfolder + \"/\" + fname;\r\n\r\n\t\t\tint i = 1;\r\n\t\t\twhile (true) {\r\n\t\t\t\tFile newfile = new File(newpath);\r\n\t\t\t\tif (!newfile.exists())\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tfname = i + orig.getName();\r\n\t\t\t\tnewpath = atfolder + \"/\" + fname;\r\n\t\t\t\ti++;\r\n\t\t\t}\r\n\r\n\t\t\tcopyFile(path, newpath);\r\n\t\t\tpath = fname;\r\n\r\n\t\t}\r\n\r\n\t\tLink at = newLink();\r\n\t\tat.setKey(-1);\r\n\t\tat.setOwnerKey(Integer.valueOf(owner.getKey()));\r\n\t\tLinkType type = typemap.get(owner.getClass());\r\n\t\tif (type == null)\r\n\t\t\tthrow new Exception(\"illegal link owner type\");\r\n\t\tat.setOwnerType(type.toString());\r\n\t\tat.setPath(path);\r\n\t\tat.setLinkType(linkType.toString());\r\n\t\tsaveLink(at);\r\n\r\n\t\t// * modification for backtrace/2way link\r\n\r\n\t\tif (linkType == LinkType.APPOINTMENT || linkType == LinkType.PROJECT\r\n\t\t\t\t|| linkType == LinkType.TASK) {\r\n\t\t\tLink at2way = newLink();\r\n\t\t\tat2way.setKey(-1);\r\n\t\t\tat2way.setOwnerKey(Integer.valueOf(at.getPath()));\r\n\t\t\tat2way.setOwnerType(at.getLinkType());\r\n\t\t\tat2way.setPath(at.getOwnerKey().toString());\r\n\t\t\tat2way.setLinkType(at.getOwnerType());\r\n\t\t\tsaveLink(at2way);\r\n\t\t}\r\n\r\n\t\t/**\r\n\t\t * Allows backtrace/2way link modification for address link if address\r\n\t\t * is not owners\r\n\t\t */\r\n\t\t// Fixes bug linking an address to itself results in a double link\r\n\t\telse if (linkType == LinkType.ADDRESS ) {\r\n\t\t\tif (!(owner instanceof Address) || !at.getOwnerKey().toString().equals(at.getPath())) {\r\n\t\t\t\tLink at2way = newLink();\r\n\t\t\t\tat2way.setKey(-1);\r\n\t\t\t\tat2way.setOwnerKey(Integer.valueOf(at.getPath()));\r\n\t\t\t\tat2way.setOwnerType(at.getLinkType());\r\n\t\t\t\tat2way.setPath(at.getOwnerKey().toString());\r\n\t\t\t\tat2way.setLinkType(at.getOwnerType());\r\n\t\t\t\tsaveLink(at2way);\r\n\t\t\t}\r\n\t\t}\r\n\t\t// end of modification\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Copy a file.\r\n\t * \r\n\t * @param fromFile\r\n\t *            the from file\r\n\t * @param toFile\r\n\t *            the to file\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tprivate static void copyFile(String fromFile, String toFile)\r\n\t\t\tthrows Exception {\r\n\t\tFileInputStream from = null;\r\n\t\tFileOutputStream to = null;\r\n\t\ttry {\r\n\t\t\tfrom = new FileInputStream(fromFile);\r\n\t\t\tto = new FileOutputStream(toFile);\r\n\t\t\tbyte[] buffer = new byte[4096];\r\n\t\t\tint bytesRead;\r\n\r\n\t\t\twhile ((bytesRead = from.read(buffer)) != -1)\r\n\t\t\t\tto.write(buffer, 0, bytesRead); // write\r\n\t\t} finally {\r\n\t\t\tif (from != null)\r\n\t\t\t\ttry {\r\n\t\t\t\t\tfrom.close();\r\n\t\t\t\t} catch (IOException e) {\r\n\t\t\t\t\t// empty\r\n\t\t\t\t}\r\n\t\t\tif (to != null)\r\n\t\t\t\ttry {\r\n\t\t\t\t\tto.close();\r\n\t\t\t\t} catch (IOException e) {\r\n\t\t\t\t\t// empty\r\n\t\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a link\r\n\t * \r\n\t * @param key\r\n\t *            the key\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void delete(int key) throws Exception {\r\n\t\tLink l = getLink(key);\r\n\t\tdelete(l);\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a link\r\n\t * \r\n\t * @param l\r\n\t *            the Link\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void delete(Link l) throws Exception {\r\n\t\tif (l.getLinkType().equals(LinkType.ATTACHMENT.toString())) {\r\n\t\t\t// delete attached file\r\n\t\t\tFile f = new File(attachmentFolder() + \"/\" + l.getPath());\r\n\t\t\tf.delete();\r\n\t\t}\r\n\t\tdb_.delete(l.getKey());\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Delete links for an owning entity\r\n\t * \r\n\t * @param owner\r\n\t *            the owning entity object\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void deleteLinksFromEntity(KeyedEntity<?> owner) throws Exception {\r\n\r\n\t\tCollection<Link> atts = getLinks(owner);\r\n\t\tIterator<Link> it = atts.iterator();\r\n\t\twhile (it.hasNext()) {\r\n\t\t\tLink at = it.next();\r\n\t\t\tdelete(at);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Delete links that target a given entity\r\n\t * \r\n\t * @param target\r\n\t *            the target entity object\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void deleteLinksToEntity(Object target) throws Exception {\r\n\r\n\t\tif (target == null)\r\n\t\t\treturn;\r\n\r\n\t\tLinkType type = typemap.get(target.getClass());\r\n\t\tif (type == null)\r\n\t\t\treturn;\r\n\r\n\t\tCollection<Link> links = getLinks();\r\n\t\tfor (Link link : links) {\r\n\t\t\tif (link.getLinkType().equals(type.toString())) {\r\n\t\t\t\tif ((type == LinkType.MEMO && ((Memo) target).getMemoName()\r\n\t\t\t\t\t\t.equals(link.getPath()))\r\n\t\t\t\t\t\t|| (type == LinkType.CHECKLIST && ((CheckList) target)\r\n\t\t\t\t\t\t\t\t.getCheckListName().equals(link.getPath()))) {\r\n\t\t\t\t\tdelete(link);\r\n\t\t\t\t} else if (target instanceof KeyedEntity<?>) {\r\n\t\t\t\t\tint key = ((KeyedEntity<?>) target).getKey();\r\n\t\t\t\t\tif (link.getPath().equals(Integer.toString(key))) {\r\n\t\t\t\t\t\tdelete(link);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Export links to XML\r\n\t * \r\n\t * @param fw\r\n\t *            the writer to write XML to\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tMarshaller m = jc.createMarshaller();\r\n\t\tXmlContainer container = new XmlContainer();\r\n\t\tcontainer.Link = getLinks();\r\n\t\tm.marshal(container, fw);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the dB.\r\n\t * \r\n\t * @return the dB\r\n\t */\r\n\tpublic EntityDB<Link> getDB() {\r\n\t\treturn (db_);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets a link.\r\n\t * \r\n\t * @param key\r\n\t *            the key\r\n\t * \r\n\t * @return the link\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Link getLink(int key) throws Exception {\r\n\t\treturn db_.readObj(key);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all links.\r\n\t * \r\n\t * @return all links\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Collection<Link> getLinks() throws Exception {\r\n\t\treturn db_.readAll();\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the links for an owning entity\r\n\t * \r\n\t * @param ownerbean\r\n\t *            the owning entity\r\n\t * \r\n\t * @return the links\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Collection<Link> getLinks(KeyedEntity<?> ownerbean) throws Exception {\r\n\t\tLinkDB adb = (LinkDB) db_;\r\n\t\tif (ownerbean == null)\r\n\t\t\treturn new ArrayList<Link>();\r\n\t\tLinkType type = typemap.get(ownerbean.getClass());\r\n\t\tif (type == null)\r\n\t\t\treturn new ArrayList<Link>();\r\n\t\treturn adb.getLinks(ownerbean.getKey(), type.toString());\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Import xml.\r\n\t * \r\n\t * @param is\r\n\t *            the input stream containing the XML\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\r\n\t\tXmlContainer container = (XmlContainer) u.unmarshal(is);\r\n\r\n\t\tif (container.Link == null)\r\n\t\t\treturn;\r\n\r\n\t\t// use key from import file if importing into empty db\r\n\t\tint nextkey = db_.nextkey();\r\n\t\tboolean use_keys = nextkey == 1;\r\n\t\tfor (Link link : container.Link) {\r\n\t\t\tif (!use_keys)\r\n\t\t\t\tlink.setKey(nextkey++);\r\n\t\t\tdb_.addObj(link);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Move links from one object to another\r\n\t * \r\n\t * @param oldOwner\r\n\t *            the old owner\r\n\t * @param newOwner\r\n\t *            the new owner\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void moveLinks(KeyedEntity<?> oldOwner, KeyedEntity<?> newOwner)\r\n\t\t\tthrows Exception {\r\n\t\tCollection<Link> atts = getLinks(oldOwner);\r\n\t\tIterator<Link> it = atts.iterator();\r\n\t\twhile (it.hasNext()) {\r\n\t\t\tLink at = it.next();\r\n\t\t\tat.setOwnerKey(Integer.valueOf(newOwner.getKey()));\r\n\t\t\tLinkType type = typemap.get(newOwner.getClass());\r\n\t\t\tif (type == null)\r\n\t\t\t\tthrow new Exception(\"illegal link owner type\");\r\n\t\t\tat.setOwnerType(type.toString());\r\n\t\t\tdb_.updateObj(at);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * return a new link object\r\n\t * \r\n\t * @return the link\r\n\t */\r\n\tpublic Link newLink() {\r\n\t\treturn (db_.newObj());\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new link model.\r\n\t */\r\n\tprivate LinkModel() {\r\n\t\tdb_ = DBHelper.getFactory().createLinkDB();\r\n\t}\r\n\r\n\t/**\r\n\t * Refresh listeners\r\n\t */\r\n\tpublic void refresh() {\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Save a link.\r\n\t * \r\n\t * @param link\r\n\t *            the link\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void saveLink(Link link) throws Exception {\r\n\t\tint num = link.getKey();\r\n\r\n\t\tif (num == -1) {\r\n\t\t\tint newkey = db_.nextkey();\r\n\t\t\tlink.setKey(newkey);\r\n\t\t\tdb_.addObj(link);\r\n\t\t} else {\r\n\t\t\tdb_.updateObj(link);\r\n\r\n\t\t}\r\n\r\n\t\t// inform views of data change\r\n\t\trefresh();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn \"LINKS\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"links\") + \": \" + getLinks().size();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/MemoModel.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003-2010 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.MemoDB;\r\nimport net.sf.borg.model.entity.Memo;\r\nimport net.sf.borg.model.undo.MemoUndoItem;\r\nimport net.sf.borg.model.undo.UndoLog;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Writer;\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.Date;\r\nimport java.util.Iterator;\r\n\r\n/**\r\n * The Memo Model manages the Memo Entities. Memos are keyed by a name. Memos contain simple text and\r\n * have stayed simple to be able to sync with the simple memo functionality of a Palm Pilot.\r\n */\r\npublic class MemoModel extends Model implements Searchable<Memo> {\r\n\r\n\t/** The normalized date format for timestamps in a memo  */\r\n\tprivate final SimpleDateFormat normalDateFormat_ = new SimpleDateFormat(\r\n\t\t\t\"MM/dd/yyyy hh:mm aa\");\r\n\r\n\t/**\r\n\t * class XmlContainer is solely for JAXB XML export/import\r\n\t * to keep the same XML structure as before JAXB was used\r\n\t */\r\n\t@XmlRootElement(name=\"MEMOS\")\r\n\tprivate static class XmlContainer {\t\t\r\n\t\tpublic Collection<Memo> Memo;\t\t\r\n\t}\r\n\t\r\n\t/** The db */\r\n\tprivate final MemoDB db_; // the database\r\n\r\n\t/** The singleton */\r\n\tstatic private final MemoModel self_ = new MemoModel();\r\n\r\n\t/**\r\n\t * Gets the singleton.\r\n\t * \r\n\t * @return the singleton\r\n\t */\r\n\tpublic static MemoModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Gets the dB.\r\n\t * \r\n\t * @return the dB\r\n\t */\r\n\tpublic MemoDB getDB() {\r\n\t\treturn db_;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all memos.\r\n\t * \r\n\t * @return all memos\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<Memo> getMemos() throws Exception {\r\n\t\tCollection<Memo> memos = db_.readAll();\r\n\t\tIterator<Memo> it = memos.iterator();\r\n\t\twhile (it.hasNext()) {\r\n\t\t\tMemo memo = it.next();\r\n\t\t\tparseOutDates(memo);\r\n\t\t}\r\n\t\treturn memos;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all memo names.\r\n\t * \r\n\t * @return the memo names\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<String> getNames() throws Exception {\r\n\r\n\t\treturn db_.getNames();\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new memo model.\r\n\t */\r\n\tprivate MemoModel() {\r\n\t\tdb_ = DBHelper.getFactory().createMemoDB();\t\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a memo by name\r\n\t * \r\n\t * @param name the memo name\r\n\t * @param undo true if we are executing an undo\r\n\t */\r\n\tpublic void delete(String name, boolean undo) {\r\n\r\n\t\ttry {\r\n\t\t\tMemo m = getMemo(name);\r\n\t\t\t\r\n\t\t\tLinkModel.getReference().deleteLinksToEntity(m);\r\n\r\n\t\t\tif (m == null)\r\n\t\t\t\treturn;\r\n\t\t\tMemo orig = m.copy();\r\n\r\n\t\t\tdb_.delete(m.getMemoName());\r\n\t\t\tif (!undo) {\r\n\t\t\t\tUndoLog.getReference().addItem(MemoUndoItem.recordDelete(orig));\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Save a memo.\r\n\t * \r\n\t * @param memo the memo\r\n\t */\r\n\tpublic void saveMemo(Memo memo) {\r\n\t\tsaveMemo(memo, false);\r\n\t}\r\n\r\n\t/**\r\n\t * Save a memo.\r\n\t * \r\n\t * @param memo the memo\r\n\t * @param undo true if we are executing an undo\r\n\t */\r\n\tpublic void saveMemo(Memo memo, boolean undo) {\r\n\r\n\t\ttry {\r\n\r\n\t\t\t// determine create an update dates\r\n\t\t\tDate now = new Date();\r\n\t\t\tif (memo.getCreated() == null)\r\n\t\t\t\tmemo.setCreated(now);\r\n\t\t\tmemo.setUpdated(now);\r\n\r\n\t\t\t// add the timestamp string to the text\r\n\t\t\taddDateString(memo);\r\n\t\t\tString name = memo.getMemoName();\r\n\t\t\tMemo old = db_.readMemo(name);\r\n\t\t\tif (old == null) {\r\n\t\t\t\tdb_.addMemo(memo);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference()\r\n\t\t\t\t\t\t\t.addItem(MemoUndoItem.recordAdd(memo));\r\n\t\t\t\t}\r\n\r\n\t\t\t} else {\r\n\t\t\t\tdb_.updateMemo(memo);\r\n\t\t\t\tif (!undo) {\r\n\t\t\t\t\tUndoLog.getReference().addItem(\r\n\t\t\t\t\t\t\tMemoUndoItem.recordUpdate(old));\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\t// inform views of data change\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Adds the timestamp string to a memo. This is just a string to hold creation and\r\n\t * last update dates. the borg ui shows these dates. The palm pilot knows nothing about them.\r\n\t * On the palm, they appear as extra memo text - and there is nowhere else to keep them\r\n\t * \r\n\t * @param m the memo\r\n\t */\r\n\tprivate void addDateString(Memo m) {\r\n\t\tif (m.getCreated() == null || m.getUpdated() == null)\r\n\t\t\treturn;\r\n\t\tif (m.getMemoText() == null)\r\n\t\t\tm.setMemoText(\"\");\r\n\t\t\r\n\t\t// if memo already has memo text - remove it\r\n\t\tString text = m.getMemoText();\r\n\t\tif (text.startsWith(\"TS;\")) {\r\n\t\t\tint idx1 = 2;\r\n\t\t\tint idx2 = text.indexOf(';', idx1 + 1);\r\n\t\t\tint idx3 = text.indexOf(';', idx2 + 1);\r\n\t\t\tif (idx2 != -1 && idx3 != -1) {\t\r\n\t\t\t\t\tm.setMemoText(text.substring(idx3 + 1));\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\t\r\n\t\tm.setMemoText(\"TS;\" + normalDateFormat_.format(m.getCreated()) + \";\"\r\n\t\t\t\t+ normalDateFormat_.format(m.getUpdated()) + \";\"\r\n\t\t\t\t+ m.getMemoText());\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Parses the timestamps.\r\n\t * \r\n\t * @param m the memp\r\n\t */\r\n\tprivate void parseOutDates(Memo m) {\r\n\r\n\t\t// separate timestamps if needed TS:created;updated;\r\n\t\tString text = m.getMemoText();\r\n\t\tif (text == null)\r\n\t\t\treturn;\r\n\t\tif (text.startsWith(\"TS;\")) {\r\n\t\t\tint idx1 = 2;\r\n\t\t\tint idx2 = text.indexOf(';', idx1 + 1);\r\n\t\t\tint idx3 = text.indexOf(';', idx2 + 1);\r\n\t\t\tif (idx2 != -1 && idx3 != -1) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tDate create = normalDateFormat_.parse(text.substring(\r\n\t\t\t\t\t\t\tidx1 + 1, idx2));\r\n\t\t\t\t\tDate update = normalDateFormat_.parse(text.substring(\r\n\t\t\t\t\t\t\tidx2 + 1, idx3));\r\n\t\t\t\t\tif (create != null)\r\n\t\t\t\t\t\tm.setCreated(create);\r\n\t\t\t\t\tif (update != null)\r\n\t\t\t\t\t\tm.setUpdated(update);\r\n\t\t\t\t\tm.setMemoText(text.substring(idx3 + 1));\r\n\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t  // empty\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Gets a memo by name.\r\n\t * \r\n\t * @param name the memo name\r\n\t * \r\n\t * @return the memo\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Memo getMemo(String name) throws Exception {\r\n\t\tMemo m = db_.readMemo(name);\r\n\r\n\t\tif (m == null)\r\n\t\t\treturn null;\r\n\t\tparseOutDates(m);\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\t/**\r\n\t * Export to XML\r\n\t * \r\n\t * @param fw the writer to write XML to\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n        Marshaller m = jc.createMarshaller();\r\n        XmlContainer container = new XmlContainer();\r\n        container.Memo = getMemos();\r\n        m.marshal(container, fw);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Import xml.\r\n\t * \r\n\t * @param is the input stream containing the XML\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\t\r\n\t\tXmlContainer container =\r\n\t\t\t  (XmlContainer)u.unmarshal(\r\n\t\t\t    is );\r\n\t\t\r\n\t\tif( container.Memo == null ) return;\r\n\r\n\t\tfor (Memo memo : container.Memo ) {\r\n\t\t\tsaveMemo(memo, true);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Refresh listeners\r\n\t */\r\n\tpublic void refresh() {\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.Searchable#search(net.sf.borg.model.SearchCriteria)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Memo> search(SearchCriteria criteria) {\r\n\t\tCollection<Memo> res = new ArrayList<Memo>(); // result collection\r\n\t\ttry {\r\n\t\t\t\r\n\t\t\t// do not match if a search category is set\r\n\t\t\tif (!criteria.getCategory().equals(\"\")\r\n\t\t\t\t\t&& !criteria.getCategory().equals(CategoryModel.UNCATEGORIZED))\r\n\t\t\t\treturn res;\r\n\r\n\t\t\tCollection<Memo> memos = getMemos();\r\n\r\n\t\t\tfor (Memo memo : memos) {\r\n\t\t\t\t\r\n\t\t\t\t// do not search on encrypted memos\r\n\t\t\t\tif( memo.isEncrypted() )\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tString tx = memo.getMemoName() + \" \" + memo.getMemoText();\r\n\t\t\t\t\r\n\t\t\t\tif( !criteria.search(tx))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t\r\n\t\t\t\t// add the appt to the search results\r\n\t\t\t\tres.add(memo);\r\n\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t\treturn (res);\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn \"MEMOS\";\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"Memos\") + \": \"\r\n\t\t+ getMemos().size();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/Model.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\n/*\r\n * Model.java\r\n *\r\n * Created on May 23, 2003, 11:33 AM\r\n */\r\n\r\npackage net.sf.borg.model;\r\n\r\nimport lombok.Data;\r\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Writer;\r\nimport java.util.ArrayList;\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\nimport java.util.logging.Level;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * base class for data models. A Model provides access to a data store to the\r\n * outside world. Clients of the model can register as Listeners to be provided\r\n * with feedback any time the model changes.\r\n */\r\npublic abstract class Model {\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t/**\r\n\t * The Class ChangeEvent.\r\n\t */\r\n\t@Data\r\n\tpublic static class ChangeEvent {\r\n\r\n\t\t/**\r\n\t\t * Enum to hold actions that can happen to an object\r\n\t\t */\r\n\t\tpublic enum ChangeAction {\r\n\t\t\tADD, CHANGE, DELETE\r\n\t\t}\r\n\r\n\t\tprivate ChangeAction action;\r\n\r\n\t\tprivate final Object object;\r\n\r\n\t\tprivate Model model;\r\n\r\n\t\t/**\r\n\t\t * Instantiates a new change event.\r\n\t\t *\r\n\t\t * @param object the changed object\r\n\t\t * @param action the action\r\n\t\t */\r\n\t\tpublic ChangeEvent(Object object, ChangeAction action) {\r\n\t\t\tthis.object = object;\r\n\t\t\tthis.action = action;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * list of all instatiated models\r\n\t */\r\n\tprivate static final Set<Model> modelList = new HashSet<Model>();\r\n\r\n\t/**\r\n\t * get a list of all instantiated models\r\n\t * \r\n\t * @return list of models\r\n\t */\r\n\tpublic static Set<Model> getExistingModels() {\r\n\t\treturn modelList;\r\n\t}\r\n\r\n\t/**\r\n\t * sync all models\r\n\t */\r\n\tpublic static void syncModels() {\r\n\t\tfor (Model m : modelList)\r\n\t\t\tm.sync();\r\n\t}\r\n\r\n\t/**\r\n\t * sync the model with the underlying DB for DBs that can be changed without\r\n\t * going through Borg (i.e. MySQL)\r\n\t */\r\n\tprotected void sync() {\r\n\t\t// do nothing by default\r\n\t}\r\n\r\n\t/**\r\n\t * Listener for a Model.\r\n\t * \r\n\t */\r\n\tpublic interface Listener {\r\n\r\n\t\t/**\r\n\t\t * Called to notify Listener when the Model is changed.\r\n\t\t */\r\n\t\tvoid update(ChangeEvent event);\r\n\r\n\t}\r\n\r\n\t// list of clients to notify when the model changes\r\n\tprivate final ArrayList<Listener> listeners;\r\n\tprivate final ArrayList<Listener> unBlockableListeners;\r\n\tprivate boolean notify_listeners = true;\r\n\r\n\t/**\r\n\t * Instantiates a new model.\r\n\t */\r\n\tpublic Model() {\r\n\t\tmodelList.add(this);\r\n\t\tlisteners = new ArrayList<Listener>();\r\n\t\tunBlockableListeners = new ArrayList<Listener>();\r\n\t}\r\n\r\n\t/**\r\n\t * Adds a listener.\r\n\t * \r\n\t * @param listener the listener\r\n\t */\r\n\tpublic void addListener(Listener listener) {\r\n\t\tlisteners.add(listener);\r\n\t}\r\n\t\r\n\tpublic void addUnblockableListener(Listener listener) {\r\n\t\tunBlockableListeners.add(listener);\r\n\t}\r\n\r\n\t/**\r\n\t * send an update message to all listeners with no change event\r\n\t */\r\n\tprotected void refreshListeners() {\r\n\t\tChangeEvent ev = new ChangeEvent(null, ChangeAction.CHANGE);\r\n\t\tev.model = this;\r\n\t\trefreshListeners(ev);\r\n\t}\r\n\r\n\t/**\r\n\t * send an update message to all listeners with a change event.\r\n\t */\r\n\tprotected void refreshListeners(ChangeEvent event) {\r\n\r\n\r\n\t\tif (log.isLoggable(Level.FINE))\r\n\t\t\tlog.fine(\"Sending ChangeEvent[\" + event.toString());\r\n\t\t\r\n\t\tfor (int i = 0; i < unBlockableListeners.size(); i++) {\r\n\t\t\tListener v = unBlockableListeners.get(i);\r\n\t\t\tv.update(event);\r\n\t\t\tif (log.isLoggable(Level.FINE))\r\n\t\t\t\tlog.fine(\"...To Listener: \" + v);\r\n\t\t}\r\n\t\t\r\n    \tif( !notify_listeners ) return;\r\n\r\n\t\tevent.model = this;\r\n\t\tfor (int i = 0; i < listeners.size(); i++) {\r\n\t\t\tListener v = listeners.get(i);\r\n\t\t\tv.update(event);\r\n\t\t\tif (log.isLoggable(Level.FINE))\r\n\t\t\t\tlog.fine(\"...To Listener: \" + v);\r\n\t\t}\r\n\t\t\t\r\n\t\r\n\t}\r\n\r\n\t/**\r\n\t * Removes the listeners.\r\n\t */\r\n\tpublic void remove() {\r\n\t\tremoveListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * Removes a listener.\r\n\t * \r\n\t * @param listener the listener\r\n\t */\r\n\tpublic void removeListener(Listener listener) {\r\n\t\tlisteners.remove(listener);\r\n\t\tunBlockableListeners.remove(listener);\r\n\t}\r\n\r\n\t/**\r\n\t * notify all listeners that the model is being destroyed\r\n\t */\r\n\tprotected void removeListeners() {\r\n\t\tlisteners.clear();\r\n\t\tunBlockableListeners.clear();\r\n\t}\r\n\r\n\t/**\r\n\t * Export the models data to XML\r\n\t * \r\n\t * @param fw - writer to write the XML to\r\n\t * @throws Exception\r\n\t */\r\n\tpublic abstract void export(Writer fw) throws Exception;\r\n\r\n\t/**\r\n\t * Import model data from XML\r\n\t * \r\n\t * @param is input stream containing XML\r\n\t * @throws Exception\r\n\t */\r\n\tpublic abstract void importXml(InputStream is) throws Exception;\r\n\r\n\t/**\r\n\t * get the root XML element name for this model's XML representation\r\n\t * \r\n\t * @return the XML root element name\r\n\t */\r\n\tpublic abstract String getExportName();\r\n\r\n\t/**\r\n\t * return user readable information about the model\r\n\t * \r\n\t * @return user readable information String\r\n\t */\r\n\tpublic abstract String getInfo() throws Exception;\r\n\r\n\tpublic void setNotifyListeners(boolean b) {\r\n\t\tnotify_listeners = b;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/OptionModel.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003-2010 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.OptionDB;\r\nimport net.sf.borg.model.entity.Option;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Writer;\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The Option Model manages the Borg Options\r\n */\r\npublic class OptionModel extends Model implements Searchable<Option> {\r\n\r\n\t/**\r\n\t * class XmlContainer is solely for JAXB XML export/import\r\n\t */\r\n\t@XmlRootElement(name = \"OPTIONS\")\r\n\tprivate static class XmlContainer {\r\n\t\tpublic Collection<Option> Option;\r\n\t}\r\n\r\n\t/** The db */\r\n\tprivate final OptionDB db_; // the database\r\n\r\n\t/** The singleton */\r\n\tstatic private final OptionModel self_ = new OptionModel();\r\n\r\n\t/**\r\n\t * Gets the singleton.\r\n\t * \r\n\t * @return the singleton\r\n\t */\r\n\tpublic static OptionModel getReference() {\r\n\t\treturn (self_);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets all options.\r\n\t * \r\n\t * @return all options\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic Collection<Option> getOptions() throws Exception {\r\n\t\treturn db_.getOptions();\r\n\t}\r\n\r\n\t\r\n\t/**\r\n\t * Instantiates a new option model.\r\n\t */\r\n\tprivate OptionModel() {\r\n\t\tdb_ = DBHelper.getFactory().createOptionDB();\r\n\t}\r\n\r\n\t\r\n\t/**\r\n\t * set an option.\r\n\t * \r\n\t * @param option\r\n\t *            the option\r\n\t * @throws Exception \r\n\t */\r\n\tpublic void setOption(Option option) throws Exception {\r\n\t\tdb_.setOption(option);\r\n\t}\r\n\r\n\t\r\n\t/**\r\n\t * Gets an option by name.\r\n\t * \r\n\t * @param name\r\n\t *            the option name\r\n\t * \r\n\t * @return the option\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic String getOption(String name) throws Exception {\r\n\t\treturn db_.getOption(name);\r\n\t}\r\n\r\n\t/**\r\n\t * Export to XML\r\n\t * \r\n\t * @param fw\r\n\t *            the writer to write XML to\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void export(Writer fw) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tMarshaller m = jc.createMarshaller();\r\n\t\tXmlContainer container = new XmlContainer();\r\n\t\tcontainer.Option = getOptions();\r\n\t\tm.marshal(container, fw);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Import xml.\r\n\t * \r\n\t * @param is\r\n\t *            the input stream containing the XML\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\t@Override\r\n\tpublic void importXml(InputStream is) throws Exception {\r\n\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\r\n\t\tXmlContainer container = (XmlContainer) u.unmarshal(is);\r\n\r\n\t\tif (container.Option == null)\r\n\t\t\treturn;\r\n\r\n\t\tfor (Option option : container.Option) {\r\n\t\t\tsetOption(option);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\t\r\n\t/**\r\n\t * import options from a collection. This code supports the old format import where the TaskModel reads\r\n\t * in the Options XML\r\n\t * @param options\r\n\t * @throws Exception \r\n\t */\r\n\tpublic void importOptions(Collection<Option> options) throws Exception\r\n\t{\r\n\t\tfor (Option option : options) {\r\n\t\t\tsetOption(option);\r\n\t\t}\r\n\r\n\t\trefresh();\r\n\t}\r\n\r\n\t/**\r\n\t * Refresh listeners\r\n\t */\r\n\tpublic void refresh() {\r\n\t\trefreshListeners();\r\n\t}\r\n\r\n\t\r\n\t@Override\r\n\tpublic String getExportName() {\r\n\t\treturn \"OPTIONS\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getInfo() throws Exception {\r\n\t\treturn Resource.getResourceString(\"Options\") + \": \"\r\n\t\t\t\t+ getOptions().size();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Collection<Option> search(SearchCriteria criteria) {\r\n\t\treturn null; \r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/ReminderTimes.java",
    "content": "package net.sf.borg.model;\r\n\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\n\r\nimport java.util.StringTokenizer;\r\n\r\n/**\r\n * This class converts the appointment reminder times to and from a string in the preferences\r\n * to an array of minutes values\r\n */\r\npublic class ReminderTimes {\r\n\r\n\t/** The times array - each element is a reminder time in minutes */\r\n\tstatic private int[] times_ = null;\r\n\t\r\n\t/** The number fo reminder times */\r\n\tstatic private final int NUM = 20;\r\n\t\r\n\t/**\r\n\t * Gets the number of reminder times\r\n\t * \r\n\t * @return the number of reminder times\r\n\t */\r\n\tstatic public int getNum()\r\n\t{\r\n\t\treturn NUM;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Gets the value (in minutes) for reminder i\r\n\t * \r\n\t * @param i the number fo the reminder to get\r\n\t * \r\n\t * @return the time in minutes\r\n\t */\r\n\tstatic public int getTimes(int i)\r\n\t{\r\n\t\tif( times_ == null )\r\n\t\t{\r\n\t\t\t// get times array from prefs if there are none loaded\r\n\t\t\ttimes_ = new int[NUM];\r\n\t\t\tString s = Prefs.getPref(PrefName.REMMINS);\r\n\t\t\tarrayFromString(s);\r\n\t\t}\r\n\t\treturn times_[i];\r\n\t}\r\n\t\r\n\t/**\r\n\t * store the reminder times\r\n\t * \r\n\t * @param times  the reminder times array\r\n\t */\r\n\tstatic public void setTimes(int[] times)\r\n\t{\r\n\t\ttimes_ = times;\r\n\t\tString s = arrayToString(times_);\r\n\t\tPrefs.putPref(PrefName.REMMINS, s);\r\n\t}\r\n\t\r\n\t/**\r\n\t * convert the reminder times array to a string for saving\r\n\t * \r\n\t * @param a the array of times\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String arrayToString(int[] a)\r\n\t{\r\n\t\tStringBuffer sb = new StringBuffer();\r\n\t\tfor( int i = 0; i < a.length; i++)\r\n\t\t{\r\n\t\t\tsb.append(a[i]);\r\n\t\t\tif( sb.length() != 0)\r\n\t\t\t\tsb.append(',');\r\n\t\t}\r\n\t\t\r\n\t\treturn sb.toString();\r\n\t}\r\n\t\r\n\t/**\r\n\t * convert a string of times to an array of integers\r\n\t * \r\n\t * @param s the s\r\n\t */\r\n\tstatic private void arrayFromString( String s)\r\n\t{\r\n\t\tStringTokenizer t = new StringTokenizer(s,\",\");\r\n\t\tfor( int i = 0; i < NUM; i++)\r\n\t\t{\r\n\t\t\tif( t.hasMoreTokens())\r\n\t\t\t{\r\n\t\t\t\tString tok = t.nextToken();\r\n\t\t\t\ttimes_[i] = Integer.parseInt(tok);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\ttimes_[i] = 0;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/Repeat.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n\tBORG is free software; you can redistribute it and/or modify\r\n\tit under the terms of the GNU General Public License as published by\r\n\tthe Free Software Foundation; either version 2 of the License, or\r\n\t(at your option) any later version.\r\n \r\n\tBORG is distributed in the hope that it will be useful,\r\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n\tGNU General Public License for more details.\r\n \r\n\tYou should have received a copy of the GNU General Public License\r\n\talong with BORG; if not, write to the Free Software\r\n\tFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model;\r\n\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.entity.Appointment;\r\n\r\nimport java.util.*;\r\n\r\n/**\r\n * A helper class for calculating all things about repeating appointments. It is\r\n * mainly an iterator that can take repeat parameters for an appointment and\r\n * iterate through the repeats\r\n */\r\npublic class Repeat {\r\n\r\n\t// the various repeat options. each constant represents a different way that\r\n\t// an appointment can repeat\r\n\tpublic static final String TTH = \"tth\";\r\n\tpublic static final String MWF = \"mwf\";\r\n\tpublic static final String WEEKENDS = \"weekends\";\r\n\tpublic static final String WEEKDAYS = \"weekdays\";\r\n\tpublic static final String YEARLY = \"yearly\";\r\n\tpublic static final String MONTHLY_DAY = \"monthly_day\";\r\n\tpublic static final String MONTHLY_DAY_LAST = \"monthly_day_last\";\r\n\tpublic static final String MONTHLY = \"monthly\";\r\n\tpublic static final String BIWEEKLY = \"biweekly\";\r\n\tpublic static final String WEEKLY = \"weekly\";\r\n\tpublic static final String DAILY = \"daily\";\r\n\tpublic static final String NDAYS = \"ndays\";\r\n\tpublic static final String NWEEKS = \"nweeks\";\r\n\tpublic static final String NMONTHS = \"nmonths\";\r\n\tpublic static final String NYEARS = \"nyears\";\r\n\tpublic static final String DAYLIST = \"dlist\";\r\n\tpublic static final String ONCE = \"once\";\r\n\r\n\tpublic final static int MAGIC_RPT_FOREVER_VALUE = 9999;\r\n\r\n\t/** the appointment date (ie the first occurrence) */\r\n\tprivate final Calendar start_;\r\n\r\n\t// scratch\r\n\tprivate final Calendar cal;\r\n\r\n\t/** The current repeat occurrence that this object is set to (via iteration) */\r\n\tprivate Calendar current_;\r\n\r\n\t/**\r\n\t * The repeat data from the appt- string that is passed in that encodes\r\n\t * frequency, particular repeat days, and a flag to indicate whether to show\r\n\t * the repeat number\r\n\t */\r\n\tprivate final String frequency_;\r\n\r\n\t/** The repeat frequency */\r\n\tprivate String freq_;\r\n\r\n\t/** the calendar field that is incrementing for each repeat */\r\n\tprivate int field;\r\n\r\n\t/** scratch to hold the day of the week in a month for certain repeat types */\r\n\tprivate int dayOfWeekMonth;\r\n\r\n\t/** scratch to hold the day of week for certain repeat types */\r\n\tprivate int dayOfWeek;\r\n\r\n\t/** The amount to increment the calendar field by for each repeat */\r\n\tprivate int incr;\r\n\r\n\t/** The frequencies string values in an array for mapping (legacy code) */\r\n\tstatic private final String[] freqs = { ONCE, DAILY, WEEKLY, BIWEEKLY, MONTHLY,\r\n\t\t\tMONTHLY_DAY, MONTHLY_DAY_LAST, YEARLY, WEEKDAYS, WEEKENDS, MWF,\r\n\t\t\tTTH, NDAYS, NWEEKS, NMONTHS, NYEARS, DAYLIST };\r\n\r\n\t/**\r\n\t * Checks to see if a particular day is valid for certain strict repeat\r\n\t * types\r\n\t * \r\n\t * @param date\r\n\t *            the date\r\n\t * @param freq\r\n\t *            the frequency\r\n\t * @param daylist\r\n\t *            the daylist (for repeat with a list of days)\r\n\t * \r\n\t * @return true, if the daye is compatible with the repeat frequency\r\n\t */\r\n\tstatic public boolean isCompatible(Calendar date, String freq,\r\n\t\t\tCollection<Integer> daylist) {\r\n\t\tString f = freqToEnglish(freq);\r\n\t\tint day = date.get(Calendar.DAY_OF_WEEK);\r\n\t\tif (f.equals(WEEKDAYS)\r\n\t\t\t\t&& (day == Calendar.SATURDAY || day == Calendar.SUNDAY))\r\n\t\t\treturn false;\r\n\t\telse if (f.equals(WEEKENDS)\r\n\t\t\t\t&& (day != Calendar.SATURDAY && day != Calendar.SUNDAY))\r\n\t\t\treturn false;\r\n\t\telse if (f.equals(MWF)\r\n\t\t\t\t&& (day != Calendar.MONDAY && day != Calendar.WEDNESDAY && day != Calendar.FRIDAY))\r\n\t\t\treturn false;\r\n\t\telse if (f.equals(TTH)\r\n\t\t\t\t&& (day != Calendar.TUESDAY && day != Calendar.THURSDAY))\r\n\t\t\treturn false;\r\n\t\telse if (f.equals(DAYLIST) && !daylist.contains(Integer.valueOf(day)))\r\n\t\t\treturn false;\r\n\t\telse if( f.equals(MONTHLY_DAY_LAST))\r\n\t\t{\r\n\t\t\tCalendar copy = new GregorianCalendar();\r\n\t\t\tcopy.setTime(date.getTime());\r\n\t\t\tint doy = date.get(Calendar.DAY_OF_YEAR);\r\n\t\t\tcopy.set(Calendar.DAY_OF_WEEK_IN_MONTH,-1);\r\n\t\t\treturn doy == copy.get(Calendar.DAY_OF_YEAR);\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * get the translated string for a frequency\r\n\t * \r\n\t * @param i\r\n\t *            the index of the frequency\r\n\t * \r\n\t * @return the translation\r\n\t */\r\n\tstatic public String getFreqString(int i) {\r\n\t\tif (i < 0 || i >= freqs.length)\r\n\t\t\treturn null;\r\n\t\treturn (Resource.getResourceString(freqs[i]));\r\n\t}\r\n\r\n\t/**\r\n\t * get the translated string for a frequency\r\n\t * \r\n\t * @param fr\r\n\t *            the internal frequency string\r\n\t * \r\n\t * @return the translation\r\n\t */\r\n\tstatic public String getFreqString(String fr) {\r\n\t\treturn (Resource.getResourceString((fr == null) ? ONCE : fr));\r\n\t}\r\n\r\n\t/**\r\n\t * convert the translated frequency string to the internal string\r\n\t * \r\n\t * @param fr\r\n\t *            the trnalsated frequency\r\n\t * \r\n\t * @return the internal frequency string\r\n\t */\r\n\tstatic public String freqToEnglish(String fr) {\r\n\t\tfor (int i = 0; i < freqs.length; i++) {\r\n\t\t\tif (fr.equals(Resource.getResourceString(freqs[i]))) {\r\n\t\t\t\treturn (freqs[i]);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn (ONCE);\r\n\t}\r\n\r\n\t/**\r\n\t * generate the encoded frequency string that is stored in the appointment -\r\n\t * that encodes frequency, daylist, and repeat number flag\r\n\t * \r\n\t * @param uistring\r\n\t *            the translated frequency string from the ui\r\n\t * @param ndays\r\n\t *            the ndays field from the UI for NDAYS repeating\r\n\t * @param rptnum\r\n\t *            the \"show repeat number\" flag\r\n\t * @param daylist\r\n\t *            the daylist for DAYLIST repeating\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic public String freqString(String uistring, Integer ndays,\r\n\t\t\tboolean rptnum, Collection<Integer> daylist) {\r\n\t\tString f = freqToEnglish(uistring);\r\n\t\tif (f.equals(NDAYS) || f.equals(NWEEKS) || f.equals(NMONTHS)\r\n\t\t\t\t|| f.equals(NYEARS)) {\r\n\t\t\tf += \",\" + ndays;\r\n\t\t}\r\n\r\n\t\tif (f.equals(DAYLIST)) {\r\n\t\t\tf += \",\";\r\n\t\t\tif (daylist != null) {\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.SUNDAY)))\r\n\t\t\t\t\tf += \"1\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.MONDAY)))\r\n\t\t\t\t\tf += \"2\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.TUESDAY)))\r\n\t\t\t\t\tf += \"3\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.WEDNESDAY)))\r\n\t\t\t\t\tf += \"4\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.THURSDAY)))\r\n\t\t\t\t\tf += \"5\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.FRIDAY)))\r\n\t\t\t\t\tf += \"6\";\r\n\t\t\t\tif (daylist.contains(Integer.valueOf(Calendar.SATURDAY)))\r\n\t\t\t\t\tf += \"7\";\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (rptnum) {\r\n\t\t\tf += \",Y\";\r\n\t\t}\r\n\r\n\t\treturn (f);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the frequency from the encoded appointment string\r\n\t * \r\n\t * @param f\r\n\t *            the frequency string from the appointment\r\n\t * \r\n\t * @return the frequency\r\n\t */\r\n\tstatic public String getFreq(String f) {\r\n\t\tif (f == null)\r\n\t\t\treturn null;\r\n\t\tint i = f.indexOf(',');\r\n\t\tif (i == -1)\r\n\t\t\treturn (f);\r\n\t\treturn (f.substring(0, i));\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the repeat number flag from the encoded appointment string\r\n\t * \r\n\t * @param f\r\n\t *            the frequency string from the appointment\r\n\t * \r\n\t * @return the repeat number flag\r\n\t */\r\n\tstatic public boolean getRptNum(String f) {\r\n\t\tif (f == null)\r\n\t\t\treturn false;\r\n\t\treturn f.endsWith(\",Y\");\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the daylist from the encoded appointment string\r\n\t * \r\n\t * @param f\r\n\t *            the frequency string from the appointment\r\n\t * \r\n\t * @return the daylist\r\n\t */\r\n\tstatic public Collection<Integer> getDaylist(String f) {\r\n\t\tArrayList<Integer> daylist = new ArrayList<Integer>();\r\n\t\tif (f == null || !f.startsWith(DAYLIST))\r\n\t\t\treturn daylist;\r\n\r\n\t\tint i2 = f.indexOf(',', DAYLIST.length() + 1);\r\n\t\tString list = null;\r\n\t\tif (i2 != -1)\r\n\t\t\tlist = f.substring(DAYLIST.length() + 1, i2);\r\n\t\telse\r\n\t\t\tlist = f.substring(DAYLIST.length() + 1);\r\n\r\n\t\tif (list.indexOf(\"1\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.SUNDAY));\r\n\t\tif (list.indexOf(\"2\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.MONDAY));\r\n\t\tif (list.indexOf(\"3\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.TUESDAY));\r\n\t\tif (list.indexOf(\"4\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.WEDNESDAY));\r\n\t\tif (list.indexOf(\"5\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.THURSDAY));\r\n\t\tif (list.indexOf(\"6\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.FRIDAY));\r\n\t\tif (list.indexOf(\"7\") != -1)\r\n\t\t\tdaylist.add(Integer.valueOf(Calendar.SATURDAY));\r\n\r\n\t\treturn (daylist);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the \"N\" multiplier value from the encoded appointment string\r\n\t * \r\n\t * @param f\r\n\t *            the encoded appointment string\r\n\t * \r\n\t * @return the \"N\" multiplier value\r\n\t */\r\n\tstatic public int getNValue(String f) {\r\n\t\tif (f == null)\r\n\t\t\treturn 0;\r\n\r\n\t\tString freq = Repeat.getFreq(f);\r\n\r\n\t\tif (!freq.equals(NDAYS) && !freq.equals(NWEEKS)\r\n\t\t\t\t&& !freq.equals(NMONTHS) && !freq.equals(NYEARS))\r\n\t\t\treturn (0);\r\n\r\n\t\tint i2 = f.indexOf(',', freq.length() + 1);\r\n\t\tif (i2 != -1)\r\n\t\t\treturn (Integer.parseInt(f.substring(freq.length() + 1, i2)));\r\n\r\n\t\treturn (Integer.parseInt(f.substring(freq.length() + 1)));\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new repeat object\r\n\t * \r\n\t * @param start\r\n\t *            the start date of the repeat (the appointment date)\r\n\t * @param frequency\r\n\t *            the frequency string from the appointment\r\n\t */\r\n\tpublic Repeat(Calendar start, String frequency) {\r\n\t\tthis.start_ = start;\r\n\t\tthis.frequency_ = frequency;\r\n\t\tcal = new GregorianCalendar(0, 0, 0);\r\n\t\tcal.setTime(start.getTime());\r\n\t\tcurrent_ = cal;\r\n\t\tincr = 1;\r\n\t\tfield = Calendar.DATE;\r\n\t\tdayOfWeek = 0;\r\n\t\tdayOfWeekMonth = 0;\r\n\r\n\t\tif (!isRepeating())\r\n\t\t\treturn;\r\n\r\n\t\t// set up the iteration parameters from the frequency\r\n\t\tfreq_ = getFreq(frequency);\r\n\t\tif (freq_.equals(WEEKLY))\r\n\t\t\tincr = 7;\r\n\t\telse if (freq_.equals(BIWEEKLY))\r\n\t\t\tincr = 14;\r\n\t\telse if (freq_.equals(MONTHLY))\r\n\t\t\tfield = Calendar.MONTH;\r\n\t\telse if (freq_.equals(MONTHLY_DAY)) {\r\n\t\t\tincr = 0;\r\n\t\t\tdayOfWeek = start.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tdayOfWeekMonth = start.get(Calendar.DAY_OF_WEEK_IN_MONTH);\r\n\t\t} else if (freq_.equals(MONTHLY_DAY_LAST)) {\r\n\t\t\tincr = 0;\r\n\t\t\tdayOfWeek = start.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tdayOfWeekMonth = -1;\r\n\t\t} else if (freq_.equals(YEARLY)) {\r\n\t\t\t// 12 months fixes the leap year condition (bug #124)\r\n\t\t\t// this is probably a bug with the java calendar not realizing next\r\n\t\t\t// year is a leap year when added\r\n\t\t\tfield = Calendar.MONTH;\r\n\t\t\tincr = 12;\r\n\t\t} else if (freq_.equals(MWF)) {\r\n\t\t\tincr = 0;\r\n\t\t} else if (freq_.equals(TTH)) {\r\n\t\t\tincr = 0;\r\n\t\t} else if (freq_.equals(NDAYS)) {\r\n\t\t\tincr = getNValue(frequency_);\r\n\t\t} else if (freq_.equals(NWEEKS)) {\r\n\t\t\tincr = 7 * getNValue(frequency_);\r\n\t\t} else if (freq_.equals(NMONTHS)) {\r\n\t\t\tincr = getNValue(frequency_);\r\n\t\t\tfield = Calendar.MONTH;\r\n\t\t} else if (freq_.equals(NYEARS)) {\r\n\t\t\t// this fixes that same leap year bug (bug #124)\r\n\t\t\t// using months instead of years\r\n\t\t\tincr = getNValue(frequency_) * 12;\r\n\t\t\tfield = Calendar.MONTH;\r\n\t\t} else if (freq_.equals(DAYLIST)) {\r\n\t\t\tincr = 0;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * \r\n\t * @return true, if this object represents a repeating item\r\n\t */\r\n\tpublic final boolean isRepeating() {\r\n\t\tString f = getFreq(frequency_);\r\n\t\treturn f != null && !f.equals(ONCE);\r\n\t}\r\n\r\n\t/**\r\n\t * Checks if an appointment repeats\r\n\t * \r\n\t * @param ap\r\n\t *            the appointment\r\n\t * \r\n\t * @return true, if the appointment is repeating\r\n\t */\r\n\tpublic static boolean isRepeating(Appointment ap) {\r\n\t\tString f = getFreq(ap.getFrequency());\r\n\t\treturn f != null && !f.equals(ONCE);\r\n\t}\r\n\r\n\t/**\r\n\t * get the current date of this iterator\r\n\t * \r\n\t * @return the current date\r\n\t */\r\n\tpublic final Calendar current() {\r\n\t\treturn current_;\r\n\t}\r\n\r\n\t/**\r\n\t * Calculate the number of a repeat given the date and the appointment\r\n\t * \r\n\t * @param current\r\n\t *            the date\r\n\t * @param appt\r\n\t *            the appointment\r\n\t * \r\n\t * @return the number of the repeat (starting with 1)\r\n\t */\r\n\tfinal static public int calculateRepeatNumber(Calendar current,\r\n\t\t\tAppointment appt) {\r\n\t\tCalendar start = new GregorianCalendar();\r\n\t\tCalendar c = start;\r\n\t\tstart.setTime(appt.getDate());\r\n\t\tRepeat r = new Repeat(start, appt.getFrequency());\r\n\t\tfor (int i = 1;; i++) {\r\n\t\t\tif ((c.get(Calendar.YEAR) == current.get(Calendar.YEAR))\r\n\t\t\t\t\t&& (c.get(Calendar.DAY_OF_YEAR) == current\r\n\t\t\t\t\t\t\t.get(Calendar.DAY_OF_YEAR)))\r\n\t\t\t\treturn (i);\r\n\t\t\tif (c.after(current))\r\n\t\t\t\treturn (0);\r\n\t\t\tc = r.next();\r\n\t\t\tif (c == null)\r\n\t\t\t\treturn (0);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * iterate to the next repeat date.\r\n\t * \r\n\t * @return the next repeat date\r\n\t */\r\n\tpublic final Calendar next() {\r\n\t\tif (!isRepeating()) {\r\n\t\t\tcurrent_ = null;\r\n\t\t\treturn current_;\r\n\t\t}\r\n\r\n\t\tcurrent_ = cal;\r\n\r\n\t\t// add the required increment\r\n\t\tif (incr != 0)\r\n\t\t\tcal.add(field, incr);\r\n\r\n\t\tif (freq_.equals(WEEKDAYS)) {\r\n\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tif (dow == Calendar.SATURDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 2);\r\n\t\t\telse if (dow == Calendar.SUNDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 1);\r\n\t\t} else if (freq_.equals(WEEKENDS)) {\r\n\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tif (dow == Calendar.MONDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 5);\r\n\t\t\telse if (dow == Calendar.TUESDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 4);\r\n\t\t\telse if (dow == Calendar.WEDNESDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 3);\r\n\t\t\telse if (dow == Calendar.THURSDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 2);\r\n\t\t\telse if (dow == Calendar.FRIDAY)\r\n\t\t\t\tcal.add(Calendar.DATE, 1);\r\n\t\t} else if (freq_.equals(MWF)) {\r\n\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tif (dow == Calendar.FRIDAY) {\r\n\t\t\t\tcal.add(Calendar.DATE, 3);\r\n\t\t\t} else {\r\n\t\t\t\tcal.add(Calendar.DATE, 2);\r\n\t\t\t}\r\n\t\t} else if (freq_.equals(TTH)) {\r\n\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\r\n\t\t\tif (dow == Calendar.THURSDAY) {\r\n\t\t\t\tcal.add(Calendar.DATE, 5);\r\n\t\t\t} else {\r\n\t\t\t\tcal.add(Calendar.DATE, 2);\r\n\t\t\t}\r\n\t\t} else if (freq_.equals(MONTHLY_DAY)) {\r\n\r\n\t\t\t// Attempt to find a date falling on the\r\n\t\t\t// same day of week and week number\r\n\t\t\t// within a subsequent month.\r\n\t\t\twhile (true) {\r\n\t\t\t\tcal.add(Calendar.MONTH, 1);\r\n\t\t\t\tcal.set(Calendar.DAY_OF_WEEK, dayOfWeek);\r\n\t\t\t\tcal.set(Calendar.DAY_OF_WEEK_IN_MONTH, dayOfWeekMonth);\r\n\t\t\t\tint dowm = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);\r\n\t\t\t\tif (dowm == dayOfWeekMonth) {\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\t// not enough days in this month\r\n\t\t} else if (freq_.equals(MONTHLY_DAY_LAST)) {\r\n\r\n\t\t\tcal.add(Calendar.MONTH, 1);\r\n\t\t\tcal.set(Calendar.DAY_OF_WEEK, dayOfWeek);\r\n\t\t\tcal.set(Calendar.DAY_OF_WEEK_IN_MONTH, -1);\r\n\r\n\t\t} else if (freq_.equals(DAYLIST)) {\r\n\t\t\tCollection<Integer> daylist = getDaylist(frequency_);\r\n\t\t\tif (daylist != null && !daylist.isEmpty()) {\r\n\t\t\t\t// advance to next day of the week in the list\r\n\t\t\t\twhile (true) {\r\n\t\t\t\t\tcal.add(Calendar.DATE, 1);\r\n\t\t\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\r\n\t\t\t\t\tif (daylist.contains(Integer.valueOf(dow))) {\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// bug fix - if repeating by month/date, must adjust if original date\r\n\t\t// was\r\n\t\t// 29, 30, or 31.\r\n\t\tint startDate = start_.get(Calendar.DATE);\r\n\t\tint maxDate = cal.getActualMaximum(Calendar.DATE);\r\n\t\tif (field == Calendar.MONTH) {\r\n\t\t\tif (startDate <= maxDate) {\r\n\t\t\t\tcal.set(Calendar.DATE, startDate);\r\n\t\t\t} else {\r\n\t\t\t\tcal.set(Calendar.DATE, maxDate);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn current_;\r\n\t}\r\n\r\n\t/**\r\n\t * Calculate the date of the last repeat of an appt\r\n\t * \r\n\t * @param appt\r\n\t * @return the date of the last repeat or null if repeats forever\r\n\t */\r\n\tstatic public Date calculateLastRepeat(Appointment appt) {\r\n\t\tif (!isRepeating(appt))\r\n\t\t\treturn appt.getDate();\r\n\r\n\t\tif (appt.getTimes() == MAGIC_RPT_FOREVER_VALUE)\r\n\t\t\treturn null;\r\n\r\n\t\tif (appt.getRepeatUntil() != null)\r\n\t\t\treturn appt.getRepeatUntil();\r\n\r\n\t\tCalendar start = new GregorianCalendar();\r\n\t\tCalendar c = start;\r\n\t\tstart.setTime(appt.getDate());\r\n\t\tRepeat r = new Repeat(start, appt.getFrequency());\r\n\t\tfor (int i = 1; i < appt.getTimes(); i++) {\r\n\t\t\tc = r.next();\r\n\t\t}\r\n\r\n\t\tif (c == null)\r\n\t\t\treturn null;\r\n\r\n\t\treturn c.getTime();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * calculate the repeat times value for an appointment based on the until\r\n\t * date or the repeat times\r\n\t * \r\n\t * @param appt\r\n\t *            the appointment\r\n\t * @return repeat times\r\n\t */\r\n\tstatic public int calculateTimes(Appointment appt) {\r\n\t\tif (!Repeat.isRepeating(appt))\r\n\t\t\treturn 1;\r\n\r\n\t\tif (appt.getRepeatUntil() == null) {\r\n\t\t\tif (appt.getTimes() != null)\r\n\t\t\t\treturn appt.getTimes().intValue();\r\n\t\t\treturn 1;\r\n\t\t}\r\n\r\n\t\tCalendar cal = new GregorianCalendar();\r\n\t\tcal.setTime(appt.getDate());\r\n\t\tRepeat repeat = new Repeat(cal, appt.getFrequency());\r\n\t\tCalendar until = new GregorianCalendar();\r\n\t\tuntil.setTime(appt.getRepeatUntil());\r\n\t\tuntil.set(Calendar.HOUR_OF_DAY, 23);\r\n\t\tuntil.set(Calendar.MINUTE, 59);\r\n\t\tint times = 0;\r\n\t\tfor (; repeat.current() != null; repeat.next()) {\r\n\t\t\tif (repeat.current().after(until))\r\n\t\t\t\tbreak;\r\n\t\t\ttimes++;\r\n\t\t}\r\n\r\n\t\treturn times;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/SearchCriteria.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.model;\n\nimport java.util.Date;\n\n/**\n * common search criteria for searching various models.\n */\npublic class SearchCriteria {\n\n\t/** The case sensitive. */\n\tprivate boolean caseSensitive = false;\n\n\t// whole word flag\n\tprivate boolean wholeWord = false;\n\n\t/** The category. */\n\tprivate String category = \"\";\n\n\t/** The end date. */\n\tprivate Date endDate = null;\n\n\t/** The has links. */\n\tprivate boolean hasLinks = false;\n\n\t/** The holiday. */\n\tprivate boolean holiday = false;\n\n\t/** The repeating. */\n\tprivate boolean repeating = false;\n\n\t/** The search string. */\n\tprivate String searchString = \"\";\n\n\t/** The start date. */\n\tprivate Date startDate = null;\n\n\t/** The todo. */\n\tprivate boolean todo = false;\n\n\t/** The vacation. */\n\tprivate boolean vacation = false;\n\n\t/**\n\t * Gets the category.\n\t * \n\t * @return the category\n\t */\n\tpublic String getCategory() {\n\t\treturn category;\n\t}\n\n\t/**\n\t * Gets the end date.\n\t * \n\t * @return the end date\n\t */\n\tpublic Date getEndDate() {\n\t\treturn endDate;\n\t}\n\n\t/**\n\t * Gets the search string.\n\t * \n\t * @return the search string\n\t */\n\tpublic String getSearchString() {\n\t\treturn searchString;\n\t}\n\n\t/**\n\t * Gets the start date.\n\t * \n\t * @return the start date\n\t */\n\tpublic Date getStartDate() {\n\t\treturn startDate;\n\t}\n\n\t/**\n\t * Checks for links.\n\t * \n\t * @return true, if successful\n\t */\n\tpublic boolean hasLinks() {\n\t\treturn hasLinks;\n\t}\n\n\t/**\n\t * Checks if is case sensitive.\n\t * \n\t * @return true, if is case sensitive\n\t */\n\tpublic boolean isCaseSensitive() {\n\t\treturn caseSensitive;\n\t}\n\n\t/**\n\t * Checks if is holiday.\n\t * \n\t * @return true, if is holiday\n\t */\n\tpublic boolean isHoliday() {\n\t\treturn holiday;\n\t}\n\n\t/**\n\t * Checks if is repeating.\n\t * \n\t * @return true, if is repeating\n\t */\n\tpublic boolean isRepeating() {\n\t\treturn repeating;\n\t}\n\n\t/**\n\t * Checks if is todo.\n\t * \n\t * @return true, if is todo\n\t */\n\tpublic boolean isTodo() {\n\t\treturn todo;\n\t}\n\n\t/**\n\t * Checks if is vacation.\n\t * \n\t * @return true, if is vacation\n\t */\n\tpublic boolean isVacation() {\n\t\treturn vacation;\n\t}\n\n\t/**\n\t * Sets the case sensitive.\n\t * \n\t * @param caseSensitive\n\t *            the new case sensitive\n\t */\n\tpublic void setCaseSensitive(boolean caseSensitive) {\n\t\tthis.caseSensitive = caseSensitive;\n\t}\n\n\t/**\n\t * Sets the category.\n\t * \n\t * @param category\n\t *            the new category\n\t */\n\tpublic void setCategory(String category) {\n\t\tthis.category = category;\n\t}\n\n\t/**\n\t * Sets the end date.\n\t * \n\t * @param endDate\n\t *            the new end date\n\t */\n\tpublic void setEndDate(Date endDate) {\n\t\tthis.endDate = endDate;\n\t}\n\n\t/**\n\t * Sets the checks for links.\n\t * \n\t * @param hasLinks\n\t *            the new checks for links\n\t */\n\tpublic void setHasLinks(boolean hasLinks) {\n\t\tthis.hasLinks = hasLinks;\n\t}\n\n\t/**\n\t * Sets the holiday.\n\t * \n\t * @param holiday\n\t *            the new holiday\n\t */\n\tpublic void setHoliday(boolean holiday) {\n\t\tthis.holiday = holiday;\n\t}\n\n\t/**\n\t * Sets the repeating.\n\t * \n\t * @param repeating\n\t *            the new repeating\n\t */\n\tpublic void setRepeating(boolean repeating) {\n\t\tthis.repeating = repeating;\n\t}\n\n\t/**\n\t * Sets the search string.\n\t * \n\t * @param searchString\n\t *            the new search string\n\t */\n\tpublic void setSearchString(String searchString) {\n\t\tthis.searchString = searchString;\n\t}\n\n\t/**\n\t * Sets the start date.\n\t * \n\t * @param startDate\n\t *            the new start date\n\t */\n\tpublic void setStartDate(Date startDate) {\n\t\tthis.startDate = startDate;\n\t}\n\n\t/**\n\t * Sets the todo.\n\t * \n\t * @param todo\n\t *            the new todo\n\t */\n\tpublic void setTodo(boolean todo) {\n\t\tthis.todo = todo;\n\t}\n\n\t/**\n\t * Sets the vacation.\n\t * \n\t * @param vacation\n\t *            the new vacation\n\t */\n\tpublic void setVacation(boolean vacation) {\n\t\tthis.vacation = vacation;\n\t}\n\n\t/**\n\t * get the whole word search flag\n\t * \n\t */\n\tpublic boolean isWholeWord() {\n\t\treturn wholeWord;\n\t}\n\n\t/**\n\t * set the whole word search flag\n\t * \n\t */\n\tpublic void setWholeWord(boolean wholeWord) {\n\t\tthis.wholeWord = wholeWord;\n\t}\n\n\t/**\n\t * do a text search on String s using any flags in the criteria (i.e. whole\n\t * word and case sensitive)\n\t * \n\t * @param s\n\t *            - source String\n\t * @return - true if there is a match\n\t */\n\tpublic boolean search(String s) {\n\t\t\n\t\tString searchStr = this.isCaseSensitive() ? this.getSearchString() : this.getSearchString().toLowerCase();\n\t\tString source = this.isCaseSensitive() ? s : s.toLowerCase();\n\n\t\tif (this.isWholeWord())\n\t\t{\n\t\t\tif( searchStr.trim().isEmpty())\n\t\t\t\treturn false;\n\t\t\treturn source.matches(\"[\\\\s\\\\S]*?\\\\b\" + searchStr + \"\\\\b[\\\\s\\\\S]*?\");\n\t\t}\n\t\t\n\t\treturn source.contains(searchStr);\n\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/Searchable.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.model;\n\nimport java.util.Collection;\n\n/**\n * interface implemented by searchable models\n *\n */\npublic interface Searchable<T> {\n\n\t/**\n\t * Return a collection of all records that match the given search criteria\n\t * @param criteria the search criteria\n\t * @return collection of matching records\n\t */\n    Collection<T> search(SearchCriteria criteria);\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/TaskModel.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.*;\r\nimport net.sf.borg.model.CategoryModel.CategorySource;\r\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.TaskDB;\r\nimport net.sf.borg.model.entity.*;\r\nimport net.sf.borg.model.undo.ProjectUndoItem;\r\nimport net.sf.borg.model.undo.SubtaskUndoItem;\r\nimport net.sf.borg.model.undo.TaskUndoItem;\r\nimport net.sf.borg.model.undo.UndoLog;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.Writer;\r\nimport java.util.*;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * TaksModel manages all of the task related entities - Task, Project, Subtask,\r\n * and Tasklog\r\n */\r\npublic class TaskModel extends Model implements Model.Listener, CategorySource,\r\n        CalendarEntityProvider, Searchable<KeyedEntity<?>> {\r\n\r\n    public static final int NO_DAYS_VALUE = 9999999;\r\n    static private final String TASKTYPES_OPTION = \"TASKTYPES\";\r\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n    /**\r\n     * The singleton\r\n     */\r\n    static private final TaskModel self_ = new TaskModel();\r\n    /**\r\n     * The db\r\n     */\r\n    private final TaskDB db_;\r\n    /**\r\n     * map of tasks keyed by due date\r\n     */\r\n    private final HashMap<Integer, Collection<Task>> btmap_;\r\n\r\n    /**\r\n     * cache of all open tasks with a due date\r\n     */\r\n    private final Vector<Task> openTaskMap;\r\n\r\n    /**\r\n     * map of subtasks keyed by due date\r\n     */\r\n    private final HashMap<Integer, Collection<Subtask>> stmap_;\r\n\r\n    /**\r\n     * map of projects keyed by due date\r\n     */\r\n    private final HashMap<Integer, Collection<Project>> pmap_;\r\n\r\n    /**\r\n     * The task types\r\n     */\r\n    private TaskTypes taskTypes_ = new TaskTypes();\r\n\r\n    /**\r\n     * Instantiates a new task model.\r\n     */\r\n    private TaskModel() {\r\n\r\n        btmap_ = new HashMap<Integer, Collection<Task>>();\r\n        stmap_ = new HashMap<Integer, Collection<Subtask>>();\r\n        pmap_ = new HashMap<Integer, Collection<Project>>();\r\n        openTaskMap = new Vector<Task>();\r\n\r\n        db_ = DBHelper.getFactory().createTaskDB();\r\n\r\n        try {\r\n            String tt = OptionModel.getReference().getOption(TASKTYPES_OPTION);\r\n            if (tt == null) {\r\n                try {\r\n                    taskTypes_.loadDefault();\r\n                    String sm = taskTypes_.toXml();\r\n                    OptionModel.getReference().setOption(\r\n                            new Option(TASKTYPES_OPTION, sm));\r\n                } catch (Exception e) {\r\n                    Errmsg.getErrorHandler().errmsg(e);\r\n                    return;\r\n                }\r\n\r\n            } else {\r\n                taskTypes_.fromString(tt);\r\n            }\r\n        } catch (Exception e) {\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n            return;\r\n        }\r\n\r\n        CategoryModel.getReference().addSource(this);\r\n        CategoryModel.getReference().addListener(this);\r\n\r\n        OptionModel.getReference().addListener(this);\r\n\r\n        this.load_map();\r\n\r\n    }\r\n\r\n    /**\r\n     * Gets the singleton.\r\n     *\r\n     * @return the singleton\r\n     */\r\n    static public TaskModel getReference() {\r\n        return (self_);\r\n    }\r\n\r\n    /*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see net.sf.borg.model.Transactional#beginTransaction()\r\n     */\r\n    public static void beginTransaction() throws Exception {\r\n        DBHelper.getController().beginTransaction();\r\n    }\r\n\r\n    /*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see net.sf.borg.model.Transactional#commitTransaction()\r\n     */\r\n    public static void commitTransaction() throws Exception {\r\n        DBHelper.getController().commitTransaction();\r\n    }\r\n\r\n    /*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see net.sf.borg.model.Transactional#rollbackTransaction()\r\n     */\r\n    public static void rollbackTransaction() throws Exception {\r\n        DBHelper.getController().rollbackTransaction();\r\n    }\r\n\r\n    /**\r\n     * Checks if a task is closed.\r\n     *\r\n     * @param t the task\r\n     * @return true, if the task is closed\r\n     */\r\n    static public boolean isClosed(Task t) {\r\n        String stat = t.getState();\r\n        String type = t.getType();\r\n        return stat.equals(TaskModel.getReference().getTaskTypes()\r\n                .getFinalState(type));\r\n    }\r\n\r\n    /**\r\n     * Checks if a project is closed.\r\n     *\r\n     * @param p the project\r\n     * @return true, if a project is closed\r\n     */\r\n    static public boolean isClosed(Project p) {\r\n        String stat = p.getStatus();\r\n        return stat.equals(Resource.getResourceString(\"CLOSED\"));\r\n    }\r\n\r\n    /**\r\n     * Gets the dB.\r\n     *\r\n     * @return the dB\r\n     */\r\n    public TaskDB getDB() {\r\n        return (db_);\r\n    }\r\n\r\n    /**\r\n     * Get all tasks due on a particular date\r\n     *\r\n     * @param d the date\r\n     * @return the tasks\r\n     */\r\n    public Collection<Task> get_tasks(Date d) {\r\n        return (btmap_.get(Integer.valueOf(DateUtil.dayOfEpoch(d))));\r\n    }\r\n\r\n    /**\r\n     * Get all subtasks due on a particular date\r\n     *\r\n     * @param d the date\r\n     * @return the subtasks\r\n     */\r\n    public Collection<Subtask> get_subtasks(Date d) {\r\n        return (stmap_.get(Integer.valueOf(DateUtil.dayOfEpoch(d))));\r\n    }\r\n\r\n    /**\r\n     * Get all projects due on a particular date\r\n     *\r\n     * @param d the date\r\n     * @return the projects\r\n     */\r\n    public Collection<Project> get_projects(Date d) {\r\n        return (pmap_.get(Integer.valueOf(DateUtil.dayOfEpoch(d))));\r\n    }\r\n\r\n    /**\r\n     * Get all open tasks with a due date\r\n     *\r\n     * @return the tasks\r\n     */\r\n    public Vector<Task> get_tasks() {\r\n        return (openTaskMap);\r\n    }\r\n\r\n    /**\r\n     * Gets the task types.\r\n     *\r\n     * @return the task types\r\n     */\r\n    public TaskTypes getTaskTypes() {\r\n        return (taskTypes_);\r\n    }\r\n\r\n    /**\r\n     * save the task types to the db\r\n     *\r\n     * @param tt the task types\r\n     * @throws Exception the exception\r\n     */\r\n    public void saveTaskTypes(TaskTypes tt) throws Exception {\r\n        if (tt != null) {\r\n            tt.validate();\r\n            taskTypes_ = tt.copy();\r\n        }\r\n        OptionModel.getReference().setOption(\r\n                new Option(TASKTYPES_OPTION, taskTypes_.toXml()));\r\n    }\r\n\r\n    /*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see net.sf.borg.model.CategoryModel.CategorySource#getCategories()\r\n     */\r\n    @Override\r\n    public Collection<String> getCategories() {\r\n\r\n        TreeSet<String> categories = new TreeSet<String>();\r\n        try {\r\n            for (Task t : db_.readAll()) {\r\n                String cat = t.getCategory();\r\n                if (cat != null && !cat.equals(\"\"))\r\n                    categories.add(cat);\r\n            }\r\n        } catch (Exception e1) {\r\n            Errmsg.getErrorHandler().errmsg(e1);\r\n        }\r\n\r\n        try {\r\n            for (Project t : getProjects()) {\r\n                String cat = t.getCategory();\r\n                if (cat != null && !cat.equals(\"\"))\r\n                    categories.add(cat);\r\n            }\r\n        } catch (Exception e) {\r\n            // ignore this one\r\n        }\r\n        return (categories);\r\n\r\n    }\r\n\r\n    /**\r\n     * Get all tasks.\r\n     *\r\n     * @return the tasks\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Task> getTasks() throws Exception {\r\n        return db_.readAll();\r\n    }\r\n\r\n    /**\r\n     * load caches of open tasks for performance. this is because the views will\r\n     * repeatedly need to retrieve tasks per day and it would be slow to\r\n     * repeatedly traverse the entire task DB looking for them only tasks that\r\n     * are viewed by day need to be cached here - those that are not CLOSED and\r\n     * have a due date - a small fraction of the total\r\n     */\r\n    private void load_map() {\r\n\r\n        // clear map\r\n        btmap_.clear();\r\n        openTaskMap.clear();\r\n        stmap_.clear();\r\n        pmap_.clear();\r\n\r\n        try {\r\n\r\n            // iterate through tasks using taskmodel\r\n            for (Task mr : getTasks()) {\r\n                // for each task, get state and skip CLOSED or PR tasks\r\n                if (mr.getState().equals(\r\n                        this.getTaskTypes().getFinalState(mr.getType())))\r\n                    continue;\r\n\r\n                if (!CategoryModel.getReference().isShown(mr.getCategory()))\r\n                    continue;\r\n\r\n                // use task due date to build a day key\r\n                Date due = mr.getDueDate();\r\n                if (due == null)\r\n                    continue;\r\n\r\n                int key = DateUtil.dayOfEpoch(due);\r\n\r\n                // add the task string to the btmap_\r\n                // add the task to the mrs_ Vector. This is used by the todo gui\r\n                Collection<Task> o = btmap_.get(Integer.valueOf(key));\r\n                if (o == null) {\r\n                    o = new LinkedList<Task>();\r\n                    btmap_.put(Integer.valueOf(key), o);\r\n                }\r\n\r\n                o.add(mr);\r\n                openTaskMap.add(mr);\r\n            }\r\n\r\n            for (Project pj : getProjects()) {\r\n                if (pj.getDueDate() == null)\r\n                    continue;\r\n\r\n                if (pj.getStatus().equals(Resource.getResourceString(\"CLOSED\")))\r\n                    continue;\r\n\r\n                if (!CategoryModel.getReference().isShown(pj.getCategory()))\r\n                    continue;\r\n\r\n                // use task due date to build a day key\r\n                Date due = pj.getDueDate();\r\n                int key = DateUtil.dayOfEpoch(due);\r\n\r\n                // add the string to the btmap_\r\n                Collection<Project> o = pmap_.get(Integer.valueOf(key));\r\n                if (o == null) {\r\n                    o = new LinkedList<Project>();\r\n                    pmap_.put(Integer.valueOf(key), o);\r\n                }\r\n\r\n                o.add(pj);\r\n            }\r\n\r\n            for (Subtask st : getSubTasks()) {\r\n                if (st.getCloseDate() != null || st.getDueDate() == null)\r\n                    continue;\r\n\r\n                Task mr = getTask(st.getTask().intValue());\r\n                String cat = mr.getCategory();\r\n                if (cat == null || cat.equals(\"\"))\r\n                    cat = CategoryModel.UNCATEGORIZED;\r\n\r\n                if (!CategoryModel.getReference().isShown(cat))\r\n                    continue;\r\n\r\n                // use task due date to build a day key\r\n                Date due = st.getDueDate();\r\n                int key = DateUtil.dayOfEpoch(due);\r\n\r\n                // add the string to the btmap_\r\n                Collection<Subtask> o = stmap_.get(Integer.valueOf(key));\r\n                if (o == null) {\r\n                    o = new LinkedList<Subtask>();\r\n                    stmap_.put(Integer.valueOf(key), o);\r\n                }\r\n\r\n                o.add(st);\r\n            }\r\n\r\n        } catch (Exception e) {\r\n\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n            return;\r\n        }\r\n\r\n    }\r\n\r\n    /**\r\n     * Delete a task\r\n     *\r\n     * @param tasknum the task id\r\n     * @throws Exception the exception\r\n     */\r\n    public void delete(int tasknum) throws Exception {\r\n        delete(tasknum, false);\r\n    }\r\n\r\n    /**\r\n     * Delete a task\r\n     *\r\n     * @param tasknum the task id\r\n     * @param undo    true if we are executing an undo\r\n     * @throws Exception the exception\r\n     */\r\n    public void delete(int tasknum, boolean undo) throws Exception {\r\n\r\n        Task task = getTask(tasknum);\r\n        try {\r\n\r\n            LinkModel.getReference().deleteLinksFromEntity(task);\r\n            LinkModel.getReference().deleteLinksToEntity(task);\r\n\r\n            if (!undo) {\r\n                UndoLog.getReference().addItem(TaskUndoItem.recordDelete(task));\r\n                // subtasks are removed by cascading delete, so set undo records\r\n                // here\r\n                Collection<Subtask> coll = getSubTasks(task.getKey());\r\n                if (coll != null) {\r\n                    for (Subtask st : coll) {\r\n                        SubtaskUndoItem.recordDelete(st);\r\n                        refreshListeners(new ChangeEvent(st, ChangeAction.DELETE));\r\n                    }\r\n                }\r\n            }\r\n            db_.delete(tasknum);\r\n        } catch (Exception e) {\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n        }\r\n\r\n        load_map();\r\n\r\n        refreshListeners(new ChangeEvent(task, ChangeAction.DELETE));\r\n\r\n    }\r\n\r\n    /**\r\n     * Delete a project.\r\n     *\r\n     * @param id the project id\r\n     * @throws Exception the exception\r\n     */\r\n    public void deleteProject(int id) throws Exception {\r\n\r\n        Project p = getProject(id);\r\n\r\n        try {\r\n\r\n            beginTransaction();\r\n\r\n            LinkModel.getReference().deleteLinksFromEntity(p);\r\n            LinkModel.getReference().deleteLinksToEntity(p);\r\n\r\n            db_.deleteProject(id);\r\n            commitTransaction();\r\n        } catch (Exception e) {\r\n            e.printStackTrace();\r\n            rollbackTransaction();\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n        }\r\n\r\n        load_map();\r\n\r\n        refreshListeners(new ChangeEvent(p, ChangeAction.DELETE));\r\n\r\n    }\r\n\r\n    /**\r\n     * Save a task.\r\n     *\r\n     * @param task the task\r\n     * @throws Exception the exception\r\n     */\r\n    public void savetask(Task task) throws Exception {\r\n        savetask(task, false);\r\n    }\r\n\r\n    /**\r\n     * Save a task.\r\n     *\r\n     * @param task the task\r\n     * @param undo true if we are executing an undo\r\n     * @throws Exception the exception\r\n     */\r\n    public void savetask(Task task, boolean undo) throws Exception {\r\n\r\n        // validations\r\n        if (task.getProject() != null) {\r\n            Project p = TaskModel.getReference().getProject(\r\n                    task.getProject().intValue());\r\n\r\n            while (p != null) {\r\n                if (task.getDueDate() != null\r\n                        && p.getDueDate() != null\r\n                        && DateUtil.isAfter(task.getDueDate(), p.getDueDate())) {\r\n                    throw new Warning(Resource.getResourceString(\"taskdd_warning\"));\r\n                }\r\n                if (TaskModel.isClosed(p) && !TaskModel.isClosed(task)) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"task_parent_closed\"));\r\n                }\r\n\r\n                Integer parent = p.getParent();\r\n                if (parent == null)\r\n                    break;\r\n                p = TaskModel.getReference().getProject(parent);\r\n            }\r\n        }\r\n\r\n        // add task to DB\r\n        Integer num = Integer.valueOf(task.getKey());\r\n        Task indb = getTask(num.intValue());\r\n\r\n        ChangeAction action = ChangeAction.ADD;\r\n\r\n        // if the task number is -1, it is a new task so\r\n        // get a new task number.\r\n        if (num.intValue() == -1 || indb == null) {\r\n            if (!undo) {\r\n                int newkey = db_.nextkey();\r\n                task.setKey(newkey);\r\n            }\r\n            task.setCreateTime(new Date());\r\n            task.setLastMod(task.getCreateTime());\r\n            if (task.getUid() == null)\r\n                task.setUid(task.getKey() + \"@BORGT-\" + task.getLastMod().getTime());\r\n            db_.addObj(task);\r\n            if (!undo) {\r\n                Task t = getTask(task.getKey());\r\n                UndoLog.getReference().addItem(TaskUndoItem.recordAdd(t));\r\n            }\r\n\r\n        } else {\r\n            // task exists - so update existing task in DB\r\n\r\n            // update close date\r\n            if (task.getState() != null && isClosed(task))\r\n                task.setCompletionDate(new Date());\r\n            int key = task.getKey();\r\n            task.setKey(key);\r\n            if (!undo) {\r\n                Task t = getTask(task.getKey());\r\n                UndoLog.getReference().addItem(TaskUndoItem.recordUpdate(t));\r\n            }\r\n            if (task.getCreateTime() == null)\r\n                task.setCreateTime(new Date());\r\n\r\n            task.setLastMod(new Date());\r\n            if (task.getUid() == null)\r\n                task.setUid(task.getKey() + \"@BORGT-\" + task.getLastMod().getTime());\r\n            \r\n            if( task.getUrl() == null && indb.getUrl() != null)\r\n            \ttask.setUrl(indb.getUrl());\r\n            db_.updateObj(task);\r\n            action = ChangeAction.CHANGE;\r\n\r\n        }\r\n\r\n        load_map();\r\n\r\n        // inform views of data change\r\n        refreshListeners(new ChangeEvent(task, action));\r\n\r\n    }\r\n\r\n    /**\r\n     * create a new task\r\n     *\r\n     * @return the task\r\n     */\r\n    public Task newMR() {\r\n        return (db_.newObj());\r\n    }\r\n\r\n    /**\r\n     * Gets a task by id.\r\n     *\r\n     * @param num the id\r\n     * @return the task\r\n     * @throws Exception the exception\r\n     */\r\n    public Task getTask(int num) throws Exception {\r\n        return (db_.readObj(num));\r\n    }\r\n\r\n    /**\r\n     * close a task\r\n     *\r\n     * @param num the task id\r\n     * @throws Exception the exception\r\n     * @throws Warning   the warning\r\n     */\r\n    public void close(int num) throws Exception, Warning {\r\n\r\n        for (Subtask st : TaskModel.getReference().getSubTasks(num)) {\r\n            if (st.getCloseDate() == null) {\r\n                throw new Warning(Resource.getResourceString(\"open_subtasks\"));\r\n            }\r\n        }\r\n\r\n        Task task = getTask(num);\r\n        task.setState(TaskModel.getReference().getTaskTypes()\r\n                .getFinalState(task.getType()));\r\n        savetask(task);\r\n    }\r\n\r\n    /**\r\n     * Close a project.\r\n     *\r\n     * @param num the project id\r\n     * @throws Exception the exception\r\n     * @throws Warning   the warning\r\n     */\r\n    public void closeProject(int num) throws Exception, Warning {\r\n\r\n        Project p = getProject(num);\r\n        p.setStatus(Resource.getResourceString(\"CLOSED\"));\r\n        saveProject(p);\r\n    }\r\n\r\n    /**\r\n     * export the task data for all tasks to XML. Also exports the options\r\n     *\r\n     * @param fw the writer to send XML to\r\n     * @throws Exception the exception\r\n     */\r\n    @Override\r\n    public void export(Writer fw) throws Exception {\r\n\r\n        JAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n        Marshaller m = jc.createMarshaller();\r\n        XmlContainer container = new XmlContainer();\r\n        container.Project = getProjects();\r\n        container.Task = getTasks();\r\n        container.Subtask = getSubTasks();\r\n        container.Tasklog = getLogs();\r\n        m.marshal(container, fw);\r\n\r\n    }\r\n\r\n    /**\r\n     * Get all projects.\r\n     *\r\n     * @return the projects\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Project> getProjects() throws Exception {\r\n        return db_.getProjects();\r\n    }\r\n\r\n    /**\r\n     * Get a project by id.\r\n     *\r\n     * @param id the id\r\n     * @return the project\r\n     * @throws Exception the exception\r\n     */\r\n    public Project getProject(int id) throws Exception {\r\n        return db_.getProject(id);\r\n    }\r\n\r\n    /**\r\n     * Import xml.\r\n     *\r\n     * @param is the input stream containing the XML\r\n     * @throws Exception the exception\r\n     */\r\n    @Override\r\n    public void importXml(InputStream is) throws Exception {\r\n\r\n        JAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n        Unmarshaller u = jc.createUnmarshaller();\r\n\r\n        XmlContainer container = (XmlContainer) u.unmarshal(is);\r\n\r\n        String dbtype = Prefs.getPref(PrefName.DBTYPE);\r\n        String url = Prefs.getPref(PrefName.JDBCURL);\r\n        if (dbtype.equals(\"jdbc\") && url != null && url.contains(\"mysql\"))\r\n            DBHelper.getController().execSQL(\"SET foreign_key_checks = 0;\");\r\n        else if (dbtype.equals(\"jdbc\") && url != null && url.contains(\"sqlite\"))\r\n            DBHelper.getController().execSQL(\"PRAGMA foreign_keys = OFF;\");\r\n        else if( dbtype.equals(\"sqlite\"))\r\n            DBHelper.getController().execSQL(\"PRAGMA foreign_keys = OFF;\");\r\n        else {\r\n            try {\r\n                DBHelper.getController()\r\n                        .execSQL(\"SET REFERENTIAL_INTEGRITY FALSE;\");\r\n            } catch (Exception e) {\r\n                log.info(e.getMessage());\r\n            }\r\n        }\r\n\r\n        try {\r\n            /*\r\n             *\r\n             * The option import code is being left in to handle old import\r\n             * files where the OPTIONS were included with the Task XML\r\n             */\r\n            if (container.OPTION != null) {\r\n                OptionModel.getReference().importOptions(container.OPTION);\r\n            }\r\n\r\n            if (container.Task != null) {\r\n                for (Task task : container.Task) {\r\n                    if (task.getPriority() == null)\r\n                        task.setPriority(Integer.valueOf(3));\r\n\r\n                    if (task.getStartDate() == null)\r\n                        task.setStartDate(new Date());\r\n\r\n                    if (task.getKey() == -1)\r\n                        task.setKey(db_.nextkey());\r\n\r\n                    if (task.getState() == null)\r\n                        task.setState(TaskModel.getReference().getTaskTypes()\r\n                                .getInitialState(task.getType()));\r\n\r\n                    db_.addObj(task);\r\n\r\n                }\r\n            }\r\n\r\n            // use key from import file if importing into empty db\r\n            int nextSubTaskKey = db_.nextSubTaskKey();\r\n            boolean use_keys = nextSubTaskKey == 1;\r\n\r\n            if (container.Subtask != null) {\r\n                for (Subtask subtask : container.Subtask) {\r\n                    if (!use_keys)\r\n                        subtask.setKey(nextSubTaskKey++);\r\n                    db_.addSubTask(subtask);\r\n                }\r\n            }\r\n\r\n            if (container.Tasklog != null) {\r\n                for (Tasklog tlog : container.Tasklog) {\r\n                    try {\r\n                        tlog.setKey(-1);\r\n                        saveLog(tlog);\r\n                    } catch (Exception e) {\r\n                        Errmsg.getErrorHandler().errmsg(e);\r\n                    }\r\n                }\r\n            }\r\n\r\n            if (container.Project != null) {\r\n                for (Project p : container.Project) {\r\n                    try {\r\n                        db_.addProject(p);\r\n                    } catch (Exception e) {\r\n                        Errmsg.getErrorHandler().errmsg(e);\r\n                    }\r\n                }\r\n            }\r\n        } finally {\r\n            if (dbtype.equals(\"jdbc\") && url != null && url.contains(\"mysql\"))\r\n                DBHelper.getController().execSQL(\"SET foreign_key_checks = 1;\");\r\n            else if (dbtype.equals(\"jdbc\") && url != null && url.contains(\"sqlite\"))\r\n                DBHelper.getController().execSQL(\"PRAGMA foreign_keys = ON;\");\r\n            else if( dbtype.equals(\"sqlite\"))\r\n                DBHelper.getController().execSQL(\"PRAGMA foreign_keys = ON;\");\r\n            else\r\n                try {\r\n                    DBHelper.getController().execSQL(\r\n                            \"SET REFERENTIAL_INTEGRITY TRUE;\");\r\n                } catch (Exception e) {\r\n                    log.info(e.getMessage());\r\n                }\r\n            load_map();\r\n            refreshListeners();\r\n        }\r\n\r\n    }\r\n\r\n    /**\r\n     * sync with db\r\n     */\r\n    @Override\r\n    public void sync() {\r\n        db_.sync();\r\n        load_map();\r\n        refreshListeners();\r\n    }\r\n\r\n    public void refresh() {\r\n        try {\r\n            load_map();\r\n            refreshListeners();\r\n        } catch (Exception e) {\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n        }\r\n    }\r\n\r\n    /*\r\n     * (non-Javadoc)\r\n     *\r\n     * @see net.sf.borg.model.Model.Listener#refresh()\r\n     */\r\n    @Override\r\n    public void update(ChangeEvent event) {\r\n        if (event.getModel() instanceof OptionModel) {\r\n            try {\r\n                String tt = OptionModel.getReference().getOption(\r\n                        TASKTYPES_OPTION);\r\n                if (tt != null) {\r\n                    taskTypes_.fromString(tt);\r\n                }\r\n            } catch (Exception e) {\r\n                Errmsg.getErrorHandler().errmsg(e);\r\n            }\r\n        } else\r\n            refresh();\r\n    }\r\n\r\n    /**\r\n     * Get the sub tasks for a task\r\n     *\r\n     * @param taskid the task id\r\n     * @return the sub tasks\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Subtask> getSubTasks(int taskid) throws Exception {\r\n        return db_.getSubTasks(taskid);\r\n\r\n    }\r\n\r\n    /**\r\n     * Get all sub tasks.\r\n     *\r\n     * @return the sub tasks\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Subtask> getSubTasks() throws Exception {\r\n        return db_.getSubTasks();\r\n    }\r\n\r\n    /**\r\n     * Get a sub task by id.\r\n     *\r\n     * @param id the subtask id\r\n     * @return the sub task\r\n     * @throws Exception the exception\r\n     */\r\n    public Subtask getSubTask(int id) throws Exception {\r\n        return db_.getSubTask(id);\r\n\r\n    }\r\n\r\n    /**\r\n     * Get all tasks for a project.\r\n     *\r\n     * @param projectid the projectid\r\n     * @return the tasks\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Task> getTasks(int projectid) throws Exception {\r\n        return db_.getTasks(projectid);\r\n    }\r\n\r\n    /**\r\n     * Get all tasks for a given type.\r\n     *\r\n     * @param type the type\r\n     * @return the tasks\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Task> getTasksByType(String type) throws Exception {\r\n        return db_.getTasksByType(type);\r\n    }\r\n\r\n    /**\r\n     * Get sub projects for a project - direct children only\r\n     *\r\n     * @param projectid the project id\r\n     * @return the sub projects\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Project> getSubProjects(int projectid) throws Exception {\r\n        return db_.getSubProjects(projectid);\r\n\r\n    }\r\n\r\n    /**\r\n     * Gets the entire project tree for a project\r\n     *\r\n     * @param projectid the root project id\r\n     * @return the all sub projects\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Project> getAllSubProjects(int projectid)\r\n            throws Exception {\r\n        Collection<Project> c = new ArrayList<Project>();\r\n        addSubProjectsToCollection(c, projectid);\r\n        return c;\r\n    }\r\n\r\n    private void addSubProjectsToCollection(Collection<Project> c, int projectid)\r\n            throws Exception {\r\n\r\n        // add my children\r\n        Collection<Project> children = getSubProjects(projectid);\r\n        if (children.isEmpty())\r\n            return;\r\n        c.addAll(children);\r\n\r\n        // add my children's children\r\n        Iterator<Project> it = children.iterator();\r\n        while (it.hasNext()) {\r\n            Project p = it.next();\r\n            addSubProjectsToCollection(c, p.getKey());\r\n        }\r\n\r\n    }\r\n\r\n    /**\r\n     * Delete a sub task.\r\n     *\r\n     * @param id the subtask id\r\n     * @throws Exception the exception\r\n     */\r\n    public void deleteSubTask(int id) throws Exception {\r\n        deleteSubTask(id, false);\r\n    }\r\n\r\n    /**\r\n     * Delete a sub task.\r\n     *\r\n     * @param id   the subtask id\r\n     * @param undo true if we are executing an undo\r\n     * @throws Exception the exception\r\n     */\r\n    public void deleteSubTask(int id, boolean undo) throws Exception {\r\n        Subtask st = db_.getSubTask(id);\r\n        if (!undo) {\r\n            SubtaskUndoItem.recordDelete(st);\r\n        }\r\n        db_.deleteSubTask(id);\r\n        load_map();\r\n\r\n        refreshListeners(new ChangeEvent(st, ChangeAction.DELETE));\r\n    }\r\n\r\n    /**\r\n     * Save a sub task.\r\n     *\r\n     * @param s the subtask\r\n     * @throws Exception the exception\r\n     */\r\n    public void saveSubTask(Subtask s) throws Exception {\r\n        saveSubTask(s, false);\r\n    }\r\n\r\n    /**\r\n     * Save a sub task.\r\n     *\r\n     * @param s    the subtask\r\n     * @param undo true if we are executing an undo\r\n     * @throws Exception the exception\r\n     */\r\n    public void saveSubTask(Subtask s, boolean undo) throws Exception {\r\n\r\n        ChangeAction action = ChangeAction.ADD;\r\n\r\n        if (s.getKey() <= 0 || null == db_.getSubTask(s.getKey())) {\r\n            if (!undo || s.getKey() == -1)\r\n                s.setKey(db_.nextSubTaskKey());\r\n            s.setCreateTime(new Date());\r\n            s.setLastMod(s.getCreateTime());\r\n            if (s.getUid() == null)\r\n                s.setUid(s.getKey() + \"@BORGS-\" + s.getLastMod().getTime());\r\n            db_.addSubTask(s);\r\n            if (!undo) {\r\n                Subtask st = db_.getSubTask(s.getKey());\r\n                SubtaskUndoItem.recordAdd(st);\r\n            }\r\n        } else {\r\n            Subtask st = db_.getSubTask(s.getKey());\r\n\r\n\r\n            s.setCreateTime(st.getCreateTime());\r\n            s.setUid(st.getUid());\r\n            \r\n            if( s.getUrl() == null )\r\n            \ts.setUrl(st.getUrl());\r\n\r\n            if (s.getCreateTime() == null)\r\n                s.setCreateTime(new Date());\r\n\r\n            s.setLastMod(new Date());\r\n            if (s.getUid() == null)\r\n                s.setUid(s.getKey() + \"@BORGS-\" + s.getLastMod().getTime());\r\n\r\n            // don't update if no difference - to prevent unneeded syncs\r\n            if (st.equals(s)) return;\r\n\r\n            if (!undo) {\r\n                SubtaskUndoItem.recordUpdate(st);\r\n            }\r\n\r\n            db_.updateSubTask(s);\r\n            action = ChangeAction.CHANGE;\r\n\r\n        }\r\n\r\n        load_map();\r\n        refreshListeners(new ChangeEvent(s, action));\r\n    }\r\n\r\n    /**\r\n     * Add a task log entry.\r\n     *\r\n     * @param taskid the task id\r\n     * @param desc   the log message\r\n     * @throws Exception the exception\r\n     */\r\n    public void addLog(int taskid, String desc) throws Exception {\r\n        db_.addLog(taskid, desc);\r\n    }\r\n\r\n    /**\r\n     * Save a task log.\r\n     *\r\n     * @param tlog the task log\r\n     * @throws Exception the exception\r\n     */\r\n    private void saveLog(Tasklog tlog) throws Exception {\r\n        db_.saveLog(tlog);\r\n    }\r\n\r\n    /**\r\n     * Get all task logs for a task.\r\n     *\r\n     * @param taskid the task id\r\n     * @return the logs\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Tasklog> getLogs(int taskid) throws Exception {\r\n        return db_.getLogs(taskid);\r\n    }\r\n\r\n    /**\r\n     * Get all task logs.\r\n     *\r\n     * @return the logs\r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Tasklog> getLogs() throws Exception {\r\n        return db_.getLogs();\r\n    }\r\n\r\n    /**\r\n     * return the days left to complete the next due item for a task\r\n     *\r\n     * @param t - the task\r\n     * @return the days left\r\n     * @throws Exception\r\n     */\r\n    public int daysLeft(Task t) throws Exception {\r\n        int daysLeft = NO_DAYS_VALUE;\r\n        if (isClosed(t))\r\n            return daysLeft;\r\n\r\n        if (t.getDueDate() != null)\r\n            daysLeft = DateUtil.daysLeft(t.getDueDate());\r\n\r\n        for (Subtask st : getSubTasks(t.getKey())) {\r\n            if (st.getCloseDate() != null)\r\n                continue;\r\n\r\n            if (st.getDueDate() != null)\r\n                daysLeft = Math.min(DateUtil.daysLeft(st.getDueDate()), daysLeft);\r\n\r\n        }\r\n        return daysLeft;\r\n\r\n    }\r\n\r\n    /**\r\n     * Save a project.\r\n     *\r\n     * @param p the project\r\n     * @throws Exception the exception\r\n     */\r\n    public void saveProject(Project p) throws Exception {\r\n        saveProject(p, false);\r\n    }\r\n\r\n    /**\r\n     * Save a project.\r\n     *\r\n     * @param p    the project\r\n     * @param undo true if we are executing an undo\r\n     * @throws Exception the exception\r\n     */\r\n    public void saveProject(Project p, boolean undo) throws Exception {\r\n\r\n        // validate no cycles in project tree\r\n        Integer pi = p.getParent();\r\n        while (pi != null) {\r\n            if (pi.intValue() == p.getKey())\r\n                throw new Warning(Resource.getResourceString(\"project_cycle\"));\r\n\r\n            Project par = TaskModel.getReference().getProject(pi);\r\n            pi = par.getParent();\r\n\r\n        }\r\n\r\n        // validation that task due dates are before project due date\r\n        if (p.getKey() != -1) {\r\n            for (Task t : TaskModel.getReference().getTasks(p.getKey())) {\r\n                if (p.getDueDate() != null && t.getDueDate() != null\r\n                        && !TaskModel.isClosed(t)\r\n                        && DateUtil.isAfter(t.getDueDate(), p.getDueDate())) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"projdd_warning\") + \": \"\r\n                                    + t.getKey());\r\n                }\r\n            }\r\n\r\n            for (Project child : TaskModel.getReference().getSubProjects(\r\n                    p.getKey())) {\r\n                if (p.getDueDate() != null && child.getDueDate() != null\r\n                        && !TaskModel.isClosed(child)\r\n                        && DateUtil.isAfter(child.getDueDate(), p.getDueDate())) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"projchild_warning\")\r\n                                    + \": \" + child.getKey());\r\n                }\r\n            }\r\n        }\r\n\r\n        // validate against parent\r\n        if (p.getParent() != null) {\r\n            Project par = TaskModel.getReference().getProject(\r\n                    p.getParent().intValue());\r\n            if (par != null) {\r\n                if (p.getDueDate() != null && par.getDueDate() != null\r\n                        && DateUtil.isAfter(p.getDueDate(), par.getDueDate())) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"projpar_warning\"));\r\n                }\r\n            }\r\n        }\r\n\r\n        if (p.getStatus().equals(Resource.getResourceString(\"CLOSED\"))) {\r\n            // make sure that all tasks are closed\r\n            for (Task pt : TaskModel.getReference().getTasks(p.getKey())) {\r\n                if (!isClosed(pt)) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"close_proj_warn\"));\r\n                }\r\n            }\r\n            for (Project pt : TaskModel.getReference().getProjects()) {\r\n                if (!isClosed(pt) && pt.getParent() != null\r\n                        && pt.getParent().intValue() == p.getKey()) {\r\n                    throw new Warning(\r\n                            Resource.getResourceString(\"close_proj_warn\"));\r\n                }\r\n            }\r\n        }\r\n\r\n        ChangeAction action = ChangeAction.ADD;\r\n\r\n        if (p.getKey() <= 0) {\r\n            if (!undo)\r\n                p.setKey(db_.nextProjectKey());\r\n            db_.addProject(p);\r\n            if (!undo) {\r\n                Project t = getProject(p.getKey());\r\n                UndoLog.getReference().addItem(ProjectUndoItem.recordAdd(t));\r\n            }\r\n        } else {\r\n            if (!undo) {\r\n                Project t = getProject(p.getKey());\r\n                UndoLog.getReference().addItem(ProjectUndoItem.recordUpdate(t));\r\n            }\r\n            db_.updateProject(p);\r\n            action = ChangeAction.CHANGE;\r\n        }\r\n\r\n        load_map();\r\n        refreshListeners(new ChangeEvent(p, action));\r\n    }\r\n\r\n    @Override\r\n    public Collection<KeyedEntity<?>> search(SearchCriteria criteria) {\r\n        Collection<KeyedEntity<?>> res = new ArrayList<KeyedEntity<?>>(); // result\r\n        // collection\r\n        try {\r\n\r\n            Collection<Project> projects = this.getProjects();\r\n            for (Project p : projects) {\r\n                if (!CategoryModel.getReference().isShown(p.getCategory()))\r\n                    continue;\r\n\r\n                String tx = p.getDescription();\r\n\r\n                if (!criteria.search(tx))\r\n                    continue;\r\n\r\n                // filter by category\r\n                if (criteria.getCategory().equals(CategoryModel.UNCATEGORIZED)\r\n                        && p.getCategory() != null\r\n                        && !p.getCategory().equals(CategoryModel.UNCATEGORIZED))\r\n                    continue;\r\n                else if (!criteria.getCategory().equals(\"\")\r\n                        && !criteria.getCategory().equals(\r\n                        CategoryModel.UNCATEGORIZED)\r\n                        && !criteria.getCategory().equals(p.getCategory()))\r\n                    continue;\r\n\r\n                // filter by links\r\n                if (criteria.hasLinks()) {\r\n                    LinkModel lm = LinkModel.getReference();\r\n                    try {\r\n                        Collection<Link> lnks = lm.getLinks(p);\r\n                        if (lnks.isEmpty())\r\n                            continue;\r\n                    } catch (Exception e) {\r\n                        Errmsg.getErrorHandler().errmsg(e);\r\n                    }\r\n                }\r\n\r\n                res.add(p);\r\n            }\r\n\r\n            Collection<Task> tasks = this.getTasks();\r\n            for (Task t : tasks) {\r\n                if (!CategoryModel.getReference().isShown(t.getCategory()))\r\n                    continue;\r\n\r\n                String tx = t.getSummary() + \" \" + t.getDescription() + \" \"\r\n                        + t.getResolution();\r\n                Collection<Subtask> subtasks = this.getSubTasks(t.getKey());\r\n                for (Subtask st : subtasks) {\r\n                    tx += \" \" + st.getDescription();\r\n                }\r\n\r\n                if (!criteria.search(tx))\r\n                    continue;\r\n\r\n                // filter by category\r\n                if (criteria.getCategory().equals(CategoryModel.UNCATEGORIZED)\r\n                        && t.getCategory() != null\r\n                        && !t.getCategory().equals(CategoryModel.UNCATEGORIZED))\r\n                    continue;\r\n                else if (!criteria.getCategory().equals(\"\")\r\n                        && !criteria.getCategory().equals(\r\n                        CategoryModel.UNCATEGORIZED)\r\n                        && !criteria.getCategory().equals(t.getCategory()))\r\n                    continue;\r\n\r\n                // filter by links\r\n                if (criteria.hasLinks()) {\r\n                    LinkModel lm = LinkModel.getReference();\r\n                    try {\r\n                        Collection<Link> lnks = lm.getLinks(t);\r\n                        if (lnks.isEmpty())\r\n                            continue;\r\n                    } catch (Exception e) {\r\n                        Errmsg.getErrorHandler().errmsg(e);\r\n                    }\r\n                }\r\n\r\n                res.add(t);\r\n\r\n                for (Subtask st : subtasks) {\r\n                    res.add(st);\r\n                }\r\n            }\r\n\r\n\r\n        } catch (Exception e) {\r\n            Errmsg.getErrorHandler().errmsg(e);\r\n        }\r\n        return (res);\r\n    }\r\n\r\n    @Override\r\n    public String getExportName() {\r\n        return \"TASKS\";\r\n    }\r\n\r\n    @Override\r\n    public String getInfo() throws Exception {\r\n        return Resource.getResourceString(\"tasks\") + \": \" + getTasks().size()\r\n                + \"\\n\" + Resource.getResourceString(\"SubTasks\") + \": \"\r\n                + getSubTasks().size() + \"\\n\"\r\n                + Resource.getResourceString(\"Logs\") + \": \" + getLogs().size()\r\n                + \"\\n\" + Resource.getResourceString(\"projects\") + \": \"\r\n                + getReference().getProjects().size();\r\n    }\r\n\r\n    @Override\r\n    public List<CalendarEntity> getEntities(Date d) {\r\n\r\n        List<CalendarEntity> ret = new ArrayList<CalendarEntity>();\r\n        GregorianCalendar cal = new GregorianCalendar();\r\n        cal.setTime(d);\r\n\r\n        // load any tasks\r\n        if (Prefs.getBoolPref(PrefName.CAL_SHOW_TASKS)) {\r\n            Collection<Project> pcol = TaskModel.getReference().get_projects(\r\n                    cal.getTime());\r\n            if (pcol != null) {\r\n                for (Project pj : pcol) {\r\n                    ret.add(pj);\r\n\r\n                }\r\n            }\r\n            Collection<Task> tasks = TaskModel.getReference().get_tasks(\r\n                    cal.getTime());\r\n            if (tasks != null) {\r\n                for (Task task : tasks) {\r\n                    ret.add(task);\r\n                }\r\n\r\n            }\r\n        }\r\n        // subtasks\r\n        if (Prefs.getBoolPref(PrefName.CAL_SHOW_SUBTASKS)) {\r\n            Collection<Subtask> sts = TaskModel.getReference().get_subtasks(\r\n                    cal.getTime());\r\n            if (sts != null) {\r\n                for (Subtask st : sts) {\r\n                    ret.add(st);\r\n                }\r\n\r\n            }\r\n        }\r\n\r\n        return ret;\r\n\r\n    }\r\n\r\n    public void renameTaskType(String oldtype, String newtype) throws Exception {\r\n        db_.renameTaskType(oldtype, newtype);\r\n        refreshListeners(new ChangeEvent(null, ChangeAction.CHANGE));\r\n\r\n    }\r\n\r\n    /**\r\n     * class XmlContainer is solely for JAXB XML export/import to keep the same\r\n     * XML structure as before JAXB was used\r\n     */\r\n    @XmlRootElement(name = \"TASKS\")\r\n    private static class XmlContainer {\r\n        public Collection<Option> OPTION; // OPTION is kept here to handle\r\n        // import of old-format import files\r\n        public Collection<Project> Project;\r\n        public Collection<Task> Task;\r\n        public Collection<Subtask> Subtask;\r\n        public Collection<Tasklog> Tasklog;\r\n    }\r\n\r\n\tpublic Task getTaskByUid(String uid) throws Exception {\r\n\t\treturn db_.getTaskByUid(uid);\r\n\t}\r\n\r\n\tpublic Subtask getSubTaskByUid(String uid) throws Exception {\r\n\t\treturn db_.getSubTaskByUid(uid);\r\n\t}\r\n\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/TaskTypes.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlAccessType;\r\nimport jakarta.xml.bind.annotation.XmlAccessorType;\r\nimport jakarta.xml.bind.annotation.XmlElement;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.common.Resource;\r\n\r\nimport java.io.InputStream;\r\nimport java.io.StringReader;\r\nimport java.io.StringWriter;\r\nimport java.net.URL;\r\nimport java.util.*;\r\n\r\n/**\r\n * The Class TaskTypes manages the Task type and state information, including\r\n * the state transitions allowed for each task type.\r\n */\r\n@XmlRootElement(name = \"TaskTypes\")\r\n@XmlAccessorType(XmlAccessType.NONE)\r\npublic class TaskTypes {\r\n\r\n\t/**\r\n\t * information about a task state\r\n\t */\r\n\tstatic private class TaskState {\r\n\t\t@XmlElement(name=\"Name\")\r\n\t\tpublic String name;\r\n\t\t@XmlElement(name=\"NextState\")\r\n\t\tpublic HashSet<String> nextStates = new HashSet<String>(); // names of the possible next states\r\n\t}\r\n\r\n\t/**\r\n\t * information about a task type\r\n\t */\r\n\tstatic private class TaskType {\r\n\t\t// default subtasks element kept for legacy reasons to prevent parse error - but is no longer used\r\n\t\t@XmlElement(name=\"DefaultSubtask\")\r\n\t\tprivate final HashSet<String> defaultSubtasks = new HashSet<String>();\r\n\t\t// final state (defualt is CLOSED)\r\n\t\t@XmlElement(name=\"FinalState\")\r\n\t\tpublic String finalState;\r\n\t\t// initial state (default is OPEN)\r\n\t\t@XmlElement(name=\"InitialState\")\r\n\t\tpublic String initialState;\r\n\t\t// type name\r\n\t\t@XmlElement(name=\"Name\")\r\n\t\tpublic String name;\r\n\t\t// the possible states for this type\r\n\t\t@XmlElement(name=\"State\")\r\n\t\tpublic HashSet<TaskState> states = new HashSet<TaskState>();\r\n\t}\r\n\r\n\t/**\r\n\t * the set of possible task types\r\n\t */\r\n\t@XmlElement(name=\"TaskType\")\r\n\tprivate HashSet<TaskType> taskTypes = new HashSet<TaskType>();\r\n\r\n\t/**\r\n\t * constructor.\r\n\t */\r\n\tpublic TaskTypes() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * add a next state transition to a state for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t * @param nextstate\r\n\t *            the nextstate\r\n\t */\r\n\tpublic void addNextState(String type, String state, String nextstate) {\r\n\r\n\t\tTaskState st = getState(type, state);\r\n\t\tif (st != null) {\r\n\t\t\tst.nextStates.add(nextstate);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Adds a state to a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t */\r\n\tpublic void addState(String type, String state) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null) {\r\n\t\t\tTaskState ts = new TaskState();\r\n\t\t\tts.name = state;\r\n\t\t\ttt.states.add(ts);\r\n\t\t}\r\n\t}\r\n\r\n\r\n\r\n\t/**\r\n\t * Adds a new type to the state model with default OPEN and CLOSE states.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t */\r\n\tpublic void addType(String type) {\r\n\r\n\t\tTaskType tt = new TaskType();\r\n\t\ttt.name = type;\r\n\t\taddState(type, \"OPEN\");\r\n\t\taddState(type, \"CLOSED\");\r\n\t\taddNextState(type, \"OPEN\", \"CLOSED\");\r\n\r\n\t\ttaskTypes.add(tt);\r\n\t\tif (getTaskTypes().contains(type))\r\n\t\t\treturn;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * change a state name for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t * @param newstate\r\n\t *            the newstate\r\n\t */\r\n\tpublic void changeState(String type, String state, String newstate) {\r\n\t\tTaskState ts = getState(type, state);\r\n\t\tif (ts != null)\r\n\t\t\tts.name = newstate;\r\n\t}\r\n\r\n\t/**\r\n\t * Change a type name.\r\n\t * \r\n\t * @param type\r\n\t *            the type name\r\n\t * @param newtype\r\n\t *            the new type name\r\n\t */\r\n\tpublic void changeType(String type, String newtype) {\r\n\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null)\r\n\t\t\ttt.name = newtype;\r\n\t}\r\n\r\n\t/**\r\n\t * Deep Copy this object.\r\n\t * \r\n\t * @return the copy\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic TaskTypes copy() throws Exception {\r\n\t\tString s = toXml();\r\n\t\tJAXBContext jc = JAXBContext.newInstance(TaskTypes.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\tTaskTypes tt = (TaskTypes) u.unmarshal(new StringReader(s));\r\n\t\treturn tt;\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a next state transition from a state for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t * @param nextstate\r\n\t *            the nextstate\r\n\t */\r\n\tpublic void deleteNextState(String type, String state, String nextstate) {\r\n\t\tTaskState st = getState(type, state);\r\n\t\tif (st != null)\r\n\t\t\tst.nextStates.remove(nextstate);\r\n\t}\r\n\r\n\t/**\r\n\t * Delete a state from a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t */\r\n\tpublic void deleteState(String type, String state) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tTaskState ts = getState(type, state);\r\n\t\tif (tt != null && ts != null)\r\n\t\t\ttt.states.remove(ts);\r\n\t}\r\n\r\n\r\n\r\n\t/**\r\n\t * Delete a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t */\r\n\tpublic void deleteType(String type) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null)\r\n\t\t\ttaskTypes.remove(tt);\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * load this TaskTypes object from an XML string. Discard any data that was already present\r\n\t * @param xmlString the XML string\r\n\t * @throws Exception\r\n\t */\r\n\tpublic void fromString(String xmlString) throws Exception {\r\n\t\tJAXBContext jc = JAXBContext.newInstance(TaskTypes.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\tTaskTypes tt = (TaskTypes) u.unmarshal(new StringReader(xmlString));\r\n\t\tthis.taskTypes = tt.taskTypes;\r\n\t}\r\n\r\n\t/**\r\n\t * load this TaskTypes object from an XML input stream. Discard any data that was already present\r\n\t * @param is the InputStream\r\n\t * @throws Exception\r\n\t */\r\n\tpublic void fromXml(InputStream is) throws Exception {\r\n\t\tJAXBContext jc = JAXBContext.newInstance(TaskTypes.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\t\tTaskTypes tt = (TaskTypes) u.unmarshal(is);\r\n\t\tthis.taskTypes = tt.taskTypes;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Gets the final state for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * \r\n\t * @return the final state\r\n\t */\r\n\tpublic String getFinalState(String type) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null && tt.finalState != null)\r\n\t\t\treturn tt.finalState;\r\n\t\treturn \"CLOSED\";\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the initial state for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * \r\n\t * @return the initial state\r\n\t */\r\n\tpublic String getInitialState(String type) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null && tt.initialState != null)\r\n\t\t\treturn tt.initialState;\r\n\t\treturn \"OPEN\";\r\n\t}\r\n\r\n\t/**\r\n\t * Gets a particular state node for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t * \r\n\t * @return the state node\r\n\t */\r\n\tprivate TaskState getState(String type, String state) {\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null) {\r\n\t\t\tfor (TaskState ts : tt.states) {\r\n\t\t\t\tif (ts.name.equals(state))\r\n\t\t\t\t\treturn ts;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the states for a given task type.\r\n\t * \r\n\t * @param type\r\n\t *            the task type\r\n\t * \r\n\t * @return the states\r\n\t */\r\n\tpublic Collection<String> getStates(String type) {\r\n\r\n\t\tArrayList<String> v = new ArrayList<String>();\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null) {\r\n\t\t\tfor (TaskState ts : tt.states) {\r\n\t\t\t\tv.add(ts.name);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn v;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Gets all task types.\r\n\t * \r\n\t * @return the task types\r\n\t */\r\n\tpublic Vector<String> getTaskTypes() {\r\n\t\tVector<String> v = new Vector<String>();\r\n\r\n\t\tfor (TaskType tt : taskTypes) {\r\n\t\t\tv.add(tt.name);\r\n\t\t}\r\n\r\n\t\treturn v;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets a type node.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * \r\n\t * @return the type node\r\n\t */\r\n\tprivate TaskType getType(String type) {\r\n\t\tfor (TaskType tt : taskTypes) {\r\n\t\t\tif (tt.name.equals(type))\r\n\t\t\t\treturn tt;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * Load the default state model XML from the borg JAR file.\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void loadDefault() throws Exception {\r\n\t\tURL tsurl = getClass().getResource(\"/task_states.xml\");\r\n\t\tfromXml(tsurl.openStream());\r\n\t}\r\n\r\n\t/**\r\n\t * get a list of possible Next states for a given state and type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t * \r\n\t * @return the vector< string>\r\n\t */\r\n\tpublic Collection<String> nextStates(String type, String state) {\r\n\r\n\t\tCollection<String> ret = new HashSet<String>();\r\n\t\tret.add(state);\r\n\t\tTaskState ts = getState(type, state);\r\n\t\tif (ts != null)\r\n\t\t\tret.addAll(ts.nextStates);\r\n\t\treturn ret;\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Sets the initial state for a type.\r\n\t * \r\n\t * @param type\r\n\t *            the type\r\n\t * @param state\r\n\t *            the state\r\n\t */\r\n\tpublic void setInitialState(String type, String state) {\r\n\r\n\t\tTaskType tt = getType(type);\r\n\t\tif (tt != null)\r\n\t\t\ttt.initialState = state;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * return the task state model as XML\r\n\t * \r\n\t * @return the XML string\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic String toXml() throws Exception {\r\n\t\tJAXBContext jc = JAXBContext.newInstance(TaskTypes.class);\r\n\t\tMarshaller m = jc.createMarshaller();\r\n\t\tStringWriter sw = new StringWriter();\r\n\t\tm.marshal(this, sw);\r\n\t\treturn sw.toString();\r\n\t}\r\n\r\n\t/**\r\n\t * validate the state model (somewhat).\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic void validate() throws Exception {\r\n\t\tCollection<String> types = getTaskTypes();\r\n\t\tIterator<String> it = types.iterator();\r\n\t\twhile (it.hasNext()) {\r\n\t\t\tString type = it.next();\r\n\t\t\tString init = getInitialState(type);\r\n\t\t\tCollection<String> states = getStates(type);\r\n\t\t\tif (init.equals(\"OPEN\") && !states.contains(\"OPEN\"))\r\n\t\t\t\tthrow new Exception(Resource.getResourceString(\"NoOpenState\")\r\n\t\t\t\t\t\t+ type);\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/Theme.java",
    "content": "/*\r\n This file is part of BORG.\r\n\r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n\r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n\r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n\r\n Copyright 2003-2011 by Mike Berger\r\n */\r\npackage net.sf.borg.model;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlAccessType;\r\nimport jakarta.xml.bind.annotation.XmlAccessorType;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport lombok.Data;\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\nimport net.sf.borg.common.Warning;\r\nimport net.sf.borg.model.Model.ChangeEvent;\r\nimport net.sf.borg.model.entity.Option;\r\n\r\nimport java.io.StringReader;\r\nimport java.io.StringWriter;\r\nimport java.util.Collection;\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\n@XmlRootElement(name = \"Theme\")\r\n@XmlAccessorType(XmlAccessType.FIELD)\r\n/**\r\n * Each Theme instance holds the values for the user tunable UI colors The Theme\r\n * class also manages the persistence of Themes in the database and keeps a\r\n * cache of Theme objects Themes are persisted as options in the OPTIONS table\r\n */\r\n@Data\r\npublic class Theme {\r\n\r\n\tstatic public final String COLOR1 = \"color1\";\r\n\tstatic public final String COLOR2 = \"color2\";\r\n\tstatic public final String COLOR3 = \"color3\";\r\n\tstatic public final String COLOR4 = \"color4\";\r\n\tstatic public final String COLOR5 = \"color5\";\r\n\tstatic public final String TASKCOLOR = \"taskColor\";\r\n\tstatic public final String BIRTHDAYCOLOR = \"birthdayColor\";\r\n\tstatic public final String HOLIDAYCOLOR = \"holidayColor\";\r\n\r\n\t// the preference which holds the name of the current (active) theme\r\n\tstatic private PrefName CURRENT_THEME = new PrefName(\"current_theme\", \"BORG\");\r\n\r\n\t// the default theme name. A default theme is always kept and cannot be\r\n\t// deleted.\r\n\t// giving the default theme a name is easier than having it empty.\r\n\tprivate final static String DEFAULT_THEME_NAME = \"BORG\";\r\n\tprivate final static String DEFAULT_DARK_THEME_NAME = \"BORG_DARK\";\r\n\tprivate final static String DEFAULT_BLACK_THEME_NAME = \"BORG_BLACK\";\r\n\r\n\t// theme cache\r\n\tprivate static Map<String, Theme> themes = null;\r\n\r\n\tstatic {\r\n\t\tnew OptionListener();\r\n\t}\r\n\r\n\t/**\r\n\t * class that listens for an import of the options and triggers a sync of the\r\n\t * themes cache\r\n\t *\r\n\t */\r\n\tprivate static class OptionListener implements Model.Listener {\r\n\r\n\t\tpublic OptionListener() {\r\n\t\t\tOptionModel.getReference().addListener(this);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void update(ChangeEvent event) {\r\n\t\t\tTheme.sync();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * convert a string containing a color to an integer code calling this method\r\n\t * should handle the value \"strike\" outside of this method if needed\r\n\t */\r\n\tpublic int colorFromString(String s) {\r\n\r\n\t\tif (s == null)\r\n\t\t\treturn getTextColor4();\r\n\r\n\t\t// includes legacy mappings of color strings to use tunable colors\r\n\t\t// should only be in this class\r\n\t\tif (s.equals(\"red\") || s.equals(COLOR1)) {\r\n\t\t\treturn getTextColor1();\r\n\t\t} else if (s.equals(\"blue\") || s.equals(COLOR2)) {\r\n\t\t\treturn getTextColor2();\r\n\t\t} else if (s.equals(\"green\") || s.equals(COLOR3)) {\r\n\t\t\treturn getTextColor3();\r\n\t\t} else if (s.equals(\"black\") || s.equals(COLOR4)) {\r\n\t\t\treturn getTextColor4();\r\n\t\t} else if (s.equals(\"white\") || s.equals(COLOR5)) {\r\n\t\t\treturn getTextColor5();\r\n\t\t} else if (s.equals(TASKCOLOR)) {\r\n\t\t\treturn getTaskTextColor();\r\n\t\t} else if (s.equals(BIRTHDAYCOLOR)) {\r\n\t\t\treturn getBirthdayTextColor();\r\n\t\t} else if (s.equals(HOLIDAYCOLOR)) {\r\n\t\t\treturn getHolidayTextColor();\r\n\t\t}\r\n\r\n\t\t// if string is a number, return it\r\n\t\ttry {\r\n\t\t\tInteger i = Integer.parseInt(s);\r\n\t\t\treturn i;\r\n\t\t} catch (Exception e) {\r\n\t\t\treturn 0; // black\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * delete a theme by name\r\n\t * \r\n\t * @param name the Theme name\r\n\t * @throws Exception\r\n\t */\r\n\tpublic static void delete(String name) throws Exception {\r\n\t\tif (name == null || name.isEmpty())\r\n\t\t\treturn;\r\n\r\n\t\t// do not allow the default themes to be deleted\r\n\t\tif (name.equals(DEFAULT_THEME_NAME) || name.equals(DEFAULT_BLACK_THEME_NAME)\r\n\t\t\t\t|| name.equals(DEFAULT_DARK_THEME_NAME))\r\n\t\t\treturn;\r\n\r\n\t\t// delete from cache\r\n\t\tif (themes == null)\r\n\t\t\tloadThemes();\r\n\t\tthemes.remove(name);\r\n\r\n\t\t// delete from db\r\n\t\tOption option = new Option(getKey(name), null);\r\n\t\tOptionModel.getReference().setOption(option);\r\n\r\n\t\t// if the active theme is deleted, then make the default active\r\n\t\tif (name.equals(Prefs.getPref(CURRENT_THEME)))\r\n\t\t\tPrefs.putPref(CURRENT_THEME, DEFAULT_THEME_NAME);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * return the active Theme\r\n\t */\r\n\tpublic static final Theme getCurrentTheme() {\r\n\t\tString cur = Prefs.getPref(CURRENT_THEME);\r\n\t\tTheme t = getTheme(cur);\r\n\r\n\t\t// if the active theme is not found(unexpected), then\r\n\t\t// create a theme with default values and return it\r\n\t\t// this is not expected to happen\r\n\t\tif (t == null) {\r\n\t\t\tt = new Theme();\r\n\t\t\tt.setName(cur);\r\n\t\t}\r\n\t\treturn t;\r\n\t}\r\n\r\n\t/**\r\n\t * map the theme name to a persistence key for a theme\r\n\t * \r\n\t * @param name - the name\r\n\t * @return - the persistence key\r\n\t */\r\n\tprivate static String getKey(String name) {\r\n\t\treturn \"THEME_\" + name;\r\n\t}\r\n\r\n\t/**\r\n\t * find a Theme by name\r\n\t * \r\n\t * @param name the Theme name\r\n\t * @return the Theme or null\r\n\t */\r\n\tpublic static final Theme getTheme(String name) {\r\n\t\tif (themes == null)\r\n\t\t\tloadThemes();\r\n\t\treturn themes.get(name);\r\n\t}\r\n\r\n\t/**\r\n\t * return a list of the persisted theme names\r\n\t */\r\n\tpublic static Collection<String> getThemeNames() {\r\n\t\tif (themes == null)\r\n\t\t\tloadThemes();\r\n\t\treturn themes.keySet();\r\n\t}\r\n\r\n\t/**\r\n\t * sync with the db. called if the options table is changed by something other\r\n\t * than the UI (such as import)\r\n\t */\r\n\tpublic static void sync() {\r\n\t\tloadThemes();\r\n\r\n\t\t// notify listeners that Prefs may have changed\r\n\t\tPrefs.notifyListeners();\r\n\t}\r\n\r\n\t/**\r\n\t * load all themes from the database into the cache. should only be called to\r\n\t * initialized the cache once\r\n\t */\r\n\tprivate static void loadThemes() {\r\n\r\n\t\tthemes = new HashMap<String, Theme>();\r\n\r\n\t\t// find all options that hold themes based on the key\r\n\t\ttry {\r\n\t\t\tCollection<Option> options = OptionModel.getReference().getOptions();\r\n\t\t\tfor (Option option : options) {\r\n\t\t\t\tif (option.getKey().startsWith(\"THEME_\")) {\r\n\t\t\t\t\t// load from XML\r\n\t\t\t\t\tJAXBContext context = JAXBContext.newInstance(Theme.class);\r\n\t\t\t\t\tUnmarshaller u = context.createUnmarshaller();\r\n\t\t\t\t\tTheme theme = (Theme) u.unmarshal(new StringReader(option.getValue()));\r\n\t\t\t\t\tthemes.put(theme.getName(), theme);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\r\n\t\t// if no default themes exist in the database, then create them\r\n\t\tif (themes.get(DEFAULT_THEME_NAME) == null) {\r\n\t\t\tTheme t = new Theme();\r\n\t\t\tt.setName(DEFAULT_THEME_NAME);\r\n\t\t\tthemes.put(t.getName(), t);\r\n\t\t\ttry {\r\n\t\t\t\tt.save();\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t}\r\n\t\t\tPrefs.putPref(CURRENT_THEME, DEFAULT_THEME_NAME);\r\n\r\n\t\t}\r\n\t\tif (themes.get(DEFAULT_DARK_THEME_NAME) == null) {\r\n\t\t\tTheme t = getDefaults(DEFAULT_DARK_THEME_NAME);\r\n\r\n\t\t\tt.setName(DEFAULT_DARK_THEME_NAME);\r\n\t\t\tthemes.put(t.getName(), t);\r\n\t\t\ttry {\r\n\t\t\t\tt.save();\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (themes.get(DEFAULT_BLACK_THEME_NAME) == null) {\r\n\t\t\tTheme t = getDefaults(DEFAULT_BLACK_THEME_NAME);\r\n\r\n\t\t\tt.setName(DEFAULT_BLACK_THEME_NAME);\r\n\t\t\tthemes.put(t.getName(), t);\r\n\t\t\ttry {\r\n\t\t\t\tt.save();\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Set a theme to be the active theme\r\n\t * \r\n\t * @param t the Theme\r\n\t * @throws Warning\r\n\t * @throws Exception\r\n\t */\r\n\tpublic static void setCurrentTheme(Theme t) throws Warning, Exception {\r\n\t\tString name = t.getName();\r\n\t\tif (name == null)\r\n\t\t\treturn;\r\n\r\n\t\t// persist the theme if it has not yet been saved\r\n\t\tif (getTheme(name) == null) {\r\n\t\t\tt.save();\r\n\t\t}\r\n\t\tPrefs.putPref(CURRENT_THEME, name);\r\n\t}\r\n\r\n\t// the Theme colors and default values\r\n\tprivate int birthdayTextColor = 10027008;\r\n\tprivate int defaultBg = 240 * 256 * 256 + 240 * 256 + 240;\r\n\tprivate int defaultFg = 0;\r\n\tprivate int halfdayBg = 13421823;\r\n\tprivate int holidayBg = 255 * 256 * 256 + 225 * 256 + 196;\r\n\tprivate int holidayTextColor = 10027212;\r\n\tprivate String name;\r\n\tprivate int reminderBg = 0xFFFF99;\r\n\tprivate int stripeBg = 15792890;\r\n\tprivate int taskTextColor = 13158;\r\n\tprivate int textColor1 = 13369395;\r\n\tprivate int textColor2 = 6684876;\r\n\tprivate int textColor3 = 39168;\r\n\tprivate int textColor4 = 13107;\r\n\tprivate int textColor5 = 16250609;\r\n\tprivate int todayBg = 255 * 256 * 256 + 200 * 256 + 200;\r\n\tprivate int vacationBg = 13434828;\r\n\tprivate int weekdayBg = 16777164;\r\n\tprivate int weekendBg = 255 * 256 * 256 + 225 * 256 + 196;\r\n\tprivate int trayIconBg = 255 * 256 * 256 + 255 * 256 + 255;\r\n\tprivate int trayIconFg = 153;\r\n\tprivate boolean pills = true;\r\n\r\n\t/**\r\n\t * save the current theme in the database\r\n\t * \r\n\t * @throws Warning\r\n\t * @throws Exception\r\n\t */\r\n\tpublic void save() throws Exception {\r\n\t\tif (name == null || name.isEmpty())\r\n\t\t\treturn;\r\n\r\n\t\tif (themes == null)\r\n\t\t\tloadThemes();\r\n\r\n\t\t// save cache entry\r\n\t\tthemes.put(name, this);\r\n\r\n\t\t// marshall to XML and persist as an option in the DB\r\n\t\tJAXBContext context = JAXBContext.newInstance(Theme.class);\r\n\t\tMarshaller m = context.createMarshaller();\r\n\t\tm.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);\r\n\t\tStringWriter sw = new StringWriter();\r\n\t\tm.marshal(this, sw);\r\n\t\tOption option = new Option(getKey(name), sw.toString());\r\n\t\tOptionModel.getReference().setOption(option);\r\n\t}\r\n\r\n\tpublic static Theme getDefaults(String name) {\r\n\t\tif (DEFAULT_DARK_THEME_NAME.equals(name)) {\r\n\t\t\tTheme t = new Theme();\r\n\t\t\tt.birthdayTextColor = -13395457;\r\n\t\t\tt.defaultBg = -12632257;\r\n\t\t\tt.defaultFg = -3355444;\r\n\t\t\tt.halfdayBg = -13421824;\r\n\t\t\tt.holidayBg = -13421773;\r\n\t\t\tt.holidayTextColor = -10066432;\r\n\t\t\tt.reminderBg = -16777216;\r\n\t\t\tt.stripeBg = -13421773;\r\n\t\t\tt.taskTextColor = -16711681;\r\n\t\t\tt.textColor1 = -52429;\r\n\t\t\tt.textColor2 = -16724788;\r\n\t\t\tt.textColor3 = -10027162;\r\n\t\t\tt.textColor4 = -3355444;\r\n\t\t\tt.textColor5 = -6710887;\r\n\t\t\tt.todayBg = -16751002;\r\n\t\t\tt.vacationBg = -16764160;\r\n\t\t\tt.weekdayBg = -12632257;\r\n\t\t\tt.weekendBg = -12632257;\r\n\t\t\tt.trayIconBg = -13421773;\r\n\t\t\tt.trayIconFg = -3355444;\r\n\t\t\tt.pills = true;\r\n\t\t\treturn t;\r\n\t\t}\r\n\t\telse if( DEFAULT_BLACK_THEME_NAME.equals(name)) {\r\n\t\t\tTheme t = new Theme();\r\n\t\t\tt.birthdayTextColor = -13395457;\r\n\t\t\tt.defaultBg = 0;\r\n\t\t\tt.defaultFg = -3355444;\r\n\t\t\tt.halfdayBg = 0;\r\n\t\t\tt.holidayBg = 0;\r\n\t\t\tt.holidayTextColor = -10066432;\r\n\t\t\tt.reminderBg = 0;\r\n\t\t\tt.stripeBg = -13421773;\r\n\t\t\tt.taskTextColor = -16711681;\r\n\t\t\tt.textColor1 = -52429;\r\n\t\t\tt.textColor2 = -16724788;\r\n\t\t\tt.textColor3 = -10027162;\r\n\t\t\tt.textColor4 = -3355444;\r\n\t\t\tt.textColor5 = -6710887;\r\n\t\t\tt.todayBg = -16751002;\r\n\t\t\tt.vacationBg = 0;\r\n\t\t\tt.weekdayBg = 0;\r\n\t\t\tt.weekendBg = 0;\r\n\t\t\tt.trayIconBg = -13421773;\r\n\t\t\tt.trayIconFg = -3355444;\r\n\t\t\tt.pills = true;\r\n\t\t\treturn t;\r\n\t\t}\r\n\t\t\r\n\t\treturn new Theme();\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/AppointmentDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.Appointment;\r\n\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\n/**\r\n * A {@link EntityDB EntityDB} subclass which allows efficient retrieval of\r\n * appointment ToDo and Repeat keys.\r\n */\r\npublic interface AppointmentDB extends EntityDB<Appointment> {\r\n\r\n\t/**\r\n\t * Gets the keys of all Appointments that are marked as Todo's.\r\n\t * \r\n\t * @return the todo keys\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n    Collection<Integer> getTodoKeys() throws Exception;\r\n\r\n\t/**\r\n\t * Gets the keys of all Appointments that are marked as repeating\r\n\t * \r\n\t * @return the repeat keys\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n    Collection<Integer> getRepeatKeys() throws Exception;\r\n\r\n\tList<Appointment> getAppointmentsByText(String text)\r\n\t\t\tthrows Exception;\r\n\r\n\tAppointment getAppointmentByUid(String uid) throws Exception;\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/CheckListDB.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.CheckList;\r\n\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The Interface for a CheckList DB.\r\n */\r\npublic interface CheckListDB {\r\n\r\n    /**\r\n     * Adds a checkList to the database.\r\n     * \r\n     * @param m the CheckList\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void addCheckList(CheckList m) throws Exception;\r\n\r\n    /**\r\n     * Delete a checkList by name.\r\n     * \r\n     * @param name the name\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void delete(String name) throws Exception;\r\n\r\n    /**\r\n     * Gets all checkList names form the db\r\n     * \r\n     * @return a collection of checkList names\r\n     * \r\n     * @throws Exception\r\n     */\r\n    Collection<String> getNames() throws Exception;\r\n\r\n    /**\r\n     * Read all checkLists from the db\r\n     * \r\n     * @return a collection of all checkLists\r\n     * \r\n     * @throws Exception\r\n     */\r\n    Collection<CheckList> readAll() throws Exception;\r\n\r\n    /**\r\n     * Read a checkList by name\r\n     * \r\n     * @param name the name\r\n     * \r\n     * @return the checkList\r\n     * \r\n     * @throws Exception\r\n     */\r\n    CheckList readCheckList(String name) throws Exception;\r\n\r\n    /**\r\n     * Update a checkList in the db.\r\n     * \r\n     * @param m the checkList\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void updateCheckList(CheckList m) throws Exception;\r\n        \r\n    \r\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/DBHelper.java",
    "content": "package net.sf.borg.model.db;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.entity.Address;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.text.DateFormat;\nimport java.util.Date;\n\n/**\n * DBHelper abstracts and hides the lower level db implementation from the model\n * It holds an application global database factory and database controller.\n * The rest of the application is not aware of the underlying DB provider\n *\n */\npublic class DBHelper {\n\t\n\t@Getter\n\t@Setter\n\tprivate static Factory factory;\n\t@Getter\n\t@Setter\n\tprivate static Controller controller;\n\t\n\t/**\n\t * check if the shutdown timestamp stored in the DB differs from the timestamp\n\t * stored in the java prefs. throw a Warning if there is a difference, which in the worst case would indicate\n\t * a failure to write the DB. One case when this can happen is when a PC is put to sleep and the OS looses\n\t * track of a removable drive containing the database. The timestamp to the java prefs succeeds, but the DB write fails\n\t * as the DB is no longer mounted. It seems that HSQL can continue running even when the OS unmounts the db filesystem (linux)\n\t * @throws Exception\n\t */\n\tpublic static void checkTimestamp() throws Exception {\n\t\tOptionDB odb = getFactory().createOptionDB();\n\t\tString option = odb.getOption(PrefName.SHUTDOWNTIME.getName());\n\t\tif (option != null\n\t\t\t\t&& !option.equals(PrefName.SHUTDOWNTIME.getDefault())) {\n\t\t\tString preftime = Prefs.getPref(PrefName.SHUTDOWNTIME);\n\t\t\tif (!preftime.equals(PrefName.SHUTDOWNTIME.getDefault())) {\n\t\n\t\t\t\t// only error if prefs have later time than DB\n\t\t\t\tlong preflong = Long.parseLong(preftime);\n\t\t\t\tlong dblong = Long.parseLong(option);\n\t\t\t\tif (preflong > dblong) {\n\t\t\t\t\tDate pdate = new Date(preflong);\n\t\t\t\t\tDate dbdate = new Date(dblong);\n\t\t\t\t\tthrow new Warning(\n\t\t\t\t\t\t\tResource.getResourceString(\"db_time_error\")\n\t\t\t\t\t\t\t\t\t+ \"\\n\\n[\"\n\t\t\t\t\t\t\t\t\t+ DateFormat.getDateTimeInstance().format(\n\t\t\t\t\t\t\t\t\t\t\tpdate)\n\t\t\t\t\t\t\t\t\t+ \" > \"\n\t\t\t\t\t\t\t\t\t+ DateFormat.getDateTimeInstance().format(\n\t\t\t\t\t\t\t\t\t\t\tdbdate) + \"]\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * interface to be implemented by all DB Factories\n\t * It contains methods to create the types of DBs used by the models\n\t *\n\t */\n\tpublic interface Factory\n\t{\n\t\tAppointmentDB createAppointmentDB();\n\t\tCheckListDB createCheckListDB();\n\t\tLinkDB createLinkDB();\n\t\tMemoDB createMemoDB();\n\t\tOptionDB createOptionDB();\n\t\tTaskDB createTaskDB();\n\t\tEntityDB<Address> createAddressDB();\n\t}\n\t\n\t/**\n\t * Interface to be implemented by all DB controllers\n\t * It contains methods that are not specific to a DB type\n\t *\n\t */\n\tpublic interface Controller\n\t{\n\t\t/**\n\t\t * build the DB URL based on the user's DB options\n\t\t */\n\t\tString buildURL();\n\t\t/**\n\t\t * perform the initial connection to the DB\n\t\t * @param url\n\t\t * @throws Exception\n\t\t */\n\t\tvoid connect(String url) throws Exception;\n\t\tvoid close() throws Exception;\n\t\tvoid execSQL(String string) throws Exception;\n\t\tResultSet execQuery(String string) throws Exception;\n\t\tvoid beginTransaction() throws Exception;\n\t\tvoid commitTransaction() throws Exception;\n\t\tvoid rollbackTransaction() throws Exception;\n\t\tConnection getConnection();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/EntityDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.KeyedEntity;\r\n\r\nimport java.util.Collection;\r\n\r\n\r\n/**\r\n * Interface for a class that provides basic ORM mapping for a KeyedEntity\r\n */\r\npublic interface EntityDB<T extends KeyedEntity<T>>\r\n{\r\n\t\r\n\t/**\r\n\t * Read all KeyedEntities of a particular type from the database.\r\n\t * \r\n\t * @return the collection of KeyedEntities\r\n\t * \r\n\t * @throws Exception\r\n\t */\r\n    Collection<T> readAll() throws  Exception;\r\n    \r\n    /**\r\n     * Read a single KeyedEntity from the database by key\r\n     * \r\n     * @param key the key\r\n     * \r\n     * @return the KeyedEntity\r\n     * \r\n     * @throws Exception \r\n     */\r\n    T readObj(int key) throws  Exception;\r\n    \r\n    /**\r\n     * Return a new instance of the KeyedEntity\r\n     * \r\n     * @return the new KeyedEntity\r\n     */\r\n    T newObj();\r\n    \r\n    /**\r\n     * Adds a KeyedEntity to the database\r\n     * \r\n     * @param entity the KeyedEntity\r\n     * \r\n     * @throws Exception \r\n     */\r\n    void addObj(T entity) throws  Exception;\r\n    \r\n    /**\r\n     * Update a KeyedEntity in the database\r\n     * \r\n     * @param entity the KeyedEntity\r\n     * \r\n     * @throws Exception \r\n     */\r\n    void updateObj(T entity) throws  Exception;\r\n    \r\n    /**\r\n     * Delete a KeyedEntity from the database\r\n     * \r\n     * @param key the key of the entity\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void delete(int key) throws Exception;\r\n    \r\n    /**\r\n     * get the next available key value for this entity type\r\n     * \r\n     * @return the next available key\r\n     * \r\n     * @throws Exception\r\n     */\r\n    int nextkey() throws Exception;\r\n    \r\n    /**\r\n     * Sync with the database (likely to just be a cache flush)\r\n     */\r\n    void sync();\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/LinkDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.Link;\r\n\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The Interface for a Link DB.\r\n */\r\npublic interface LinkDB extends EntityDB<Link>\r\n{\r\n\t\r\n\t/**\r\n\t * Gets all links for a given Entity.\r\n\t * \r\n\t * @param ownerkey the key of the Entity\r\n\t * @param ownertype the type of Entity\r\n\t * \r\n\t * @return the collection of links\r\n\t * \r\n\t * @throws Exception\r\n\t */\r\n    Collection<Link> getLinks(int ownerkey, String ownertype) throws Exception;\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/MemoDB.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.Memo;\r\n\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The Interface for a Memo DB.\r\n */\r\npublic interface MemoDB {\r\n\r\n    /**\r\n     * Adds a memo to the database.\r\n     * \r\n     * @param m the Memo\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void addMemo(Memo m) throws Exception;\r\n\r\n    /**\r\n     * Delete a memo by name.\r\n     * \r\n     * @param name the name\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void delete(String name) throws Exception;\r\n\r\n    /**\r\n     * Gets all memo names form the db\r\n     * \r\n     * @return a collection of memo names\r\n     * \r\n     * @throws Exception\r\n     */\r\n    Collection<String> getNames() throws Exception;\r\n\r\n    /**\r\n     * Read all memos from the db\r\n     * \r\n     * @return a collection of all memos\r\n     * \r\n     * @throws Exception\r\n     */\r\n    Collection<Memo> readAll() throws Exception;\r\n\r\n    /**\r\n     * Read a memo by name\r\n     * \r\n     * @param name the name\r\n     * \r\n     * @return the memo\r\n     * \r\n     * @throws Exception\r\n     */\r\n    Memo readMemo(String name) throws Exception;\r\n\r\n    /**\r\n     * Update a memo in the db.\r\n     * \r\n     * @param m the memo\r\n     * \r\n     * @throws Exception\r\n     */\r\n    void updateMemo(Memo m) throws Exception;\r\n        \r\n    \r\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/OptionDB.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.Option;\r\n\r\nimport java.util.Collection;\r\n\r\n/**\r\n * The Interface for an Option DB.\r\n */\r\npublic interface OptionDB {\r\n\r\n\t/**\r\n\t * Gets an option value from the options table\r\n\t * \r\n\t * @param oname\r\n\t *            the option name\r\n\t * \r\n\t * @return the option value\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n    String getOption(String oname) throws Exception;\r\n\r\n\t/**\r\n\t * Gets all options from the options table.\r\n\t * \r\n\t * @return a collection of options\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n    Collection<Option> getOptions() throws Exception;\r\n\r\n\t/**\r\n\t * Sets an option in the options table.\r\n\t * \r\n\t * @param option\r\n\t *            the option to set\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n    void setOption(Option option) throws Exception;\r\n\r\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/TaskDB.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db;\r\n\r\nimport net.sf.borg.model.entity.Project;\r\nimport net.sf.borg.model.entity.Subtask;\r\nimport net.sf.borg.model.entity.Task;\r\nimport net.sf.borg.model.entity.Tasklog;\r\n\r\nimport java.util.Collection;\r\n\r\n\r\n/**\r\n * The Interface for a Task database.\r\n */\r\npublic interface TaskDB extends EntityDB<Task>{\r\n\r\n    /**\r\n     * Gets all sub tasks for a given task.\r\n     * \r\n     * @param taskid the taskid\r\n     * \r\n     * @return the sub tasks\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Subtask> getSubTasks(int taskid) throws Exception;\r\n    \r\n    /**\r\n     * Gets all sub tasks in the database.\r\n     * \r\n     * @return the sub tasks\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Subtask> getSubTasks() throws Exception;\r\n    \r\n    /**\r\n     * Gets a sub task by subtask id.\r\n     * \r\n     * @param id the id\r\n     * \r\n     * @return the sub task\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Subtask getSubTask(int id) throws Exception;\r\n\r\n    /**\r\n     * Delete a sub task by id.\r\n     * \r\n     * @param id the id\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void deleteSubTask(int id) throws Exception;\r\n\r\n    /**\r\n     * Adds a sub task.\r\n     * \r\n     * @param s the subtask\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void addSubTask(Subtask s) throws Exception;\r\n    \r\n    /**\r\n     * Update a sub task.\r\n     * \r\n     * @param s the subtask\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void updateSubTask(Subtask s) throws Exception;\r\n    \r\n    /**\r\n     * get the Next available sub task key.\r\n     * \r\n     * @return the next available sub task key\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    int nextSubTaskKey() throws Exception;\r\n    \r\n    /**\r\n     * Gets all task logs for a given task.\r\n     * \r\n     * @param taskid the task id\r\n     * \r\n     * @return the logs\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Tasklog> getLogs(int taskid) throws Exception;\r\n    \r\n    /**\r\n     * Gets all task logs in the db.\r\n     * \r\n     * @return the logs\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Tasklog> getLogs() throws Exception;\r\n    \r\n    /**\r\n     * Adds a task log for a task.\r\n     * \r\n     * @param taskid the task id\r\n     * @param desc the log text\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void addLog(int taskid, String desc) throws Exception;\r\n    \r\n    /**\r\n     * Save a task log in the db.\r\n     * \r\n     * @param tlog the task log object\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void saveLog(Tasklog tlog) throws Exception;\r\n    \r\n    /**\r\n     * Gets all projects in the database.\r\n     * \r\n     * @return the projects\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Project> getProjects() throws Exception;\r\n    \r\n    /**\r\n     * Gets all tasks for a given project.\r\n     * \r\n     * @param projectid the project id\r\n     * \r\n     * @return the tasks\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Task> getTasks(int projectid) throws Exception;\r\n    \r\n    /**\r\n     * Gets a project by id.\r\n     * \r\n     * @param projectid the project id\r\n     * \r\n     * @return the project\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Project getProject(int projectid) throws Exception;\r\n    \r\n    /**\r\n     * Delete a project by id \r\n     * \r\n     * @param id the project id\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void deleteProject(int id) throws Exception;\r\n\r\n    /**\r\n     * Adds a project to the db.\r\n     * \r\n     * @param p the project\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void addProject(Project p) throws Exception;\r\n    \r\n    /**\r\n     * Updates a project in the db.\r\n     * \r\n     * @param p the project\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    void updateProject(Project p) throws Exception;\r\n    \r\n    /**\r\n     * get the Next available project key.\r\n     * \r\n     * @return the next available project key\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    int nextProjectKey() throws Exception;\r\n    \r\n    /**\r\n     * Gets all sub projects (child projects) for a given project.\r\n     * \r\n     * @param projid the project id\r\n     * \r\n     * @return the sub projects\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    Collection<Project> getSubProjects(int projid) throws Exception;\r\n    \r\n    /**\r\n     * get all tasks with a given type\r\n     * @param type the type\r\n     * @return the tasks\r\n     * @throws Exception\r\n     */\r\n    Collection<Task> getTasksByType(String type) throws Exception;\r\n    \r\n    /**\r\n     * rename a task type in existing tasks\r\n     * @param oldtype\r\n     * @param newtype\r\n     * @throws Exception\r\n     */\r\n    void renameTaskType(String oldtype, String newtype) throws Exception;\r\n\r\n\tSubtask getSubTaskByUid(String uid) throws Exception;\r\n\r\n\tTask getTaskByUid(String uid) throws Exception;\r\n\r\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/AddrJdbcDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.model.db.EntityDB;\r\nimport net.sf.borg.model.entity.Address;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\n\r\n\r\n/**\r\n * this is the JDBC layer for access to the addresses table.\r\n */\r\nclass AddrJdbcDB extends JdbcBeanDB<Address> implements EntityDB<Address>\r\n{       \r\n\t\r\n\tpublic AddrJdbcDB()\r\n\t{\r\n\t\tnew JdbcDBUpgrader(\"select vcard from addresses\",\r\n\t\t\t\t\"alter table addresses add column vcard longvarchar\")\r\n\t\t\t\t.upgrade();\r\n\r\n\t}\r\n    \r\n    /* (non-Javadoc)\r\n     * @see net.sf.borg.model.db.EntityDB#addObj(net.sf.borg.model.entity.KeyedEntity)\r\n     */\r\n    @Override\r\n    public void addObj(Address addr) throws Exception\r\n    {\r\n        PreparedStatement stmt = JdbcDB.getConnection().prepareStatement( \"INSERT INTO addresses ( address_num, \" +\r\n        \"first_name, last_name, nickname, email, screen_name, work_phone,\" + \r\n        \"home_phone, fax, pager, street, city, state, zip, country, company,\" +\r\n        \"work_street, work_city, work_state, work_zip, work_country, webpage, notes, birthday, cell_phone, vcard ) \" +\r\n        \" VALUES \" +\r\n        \"( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )\");\r\n        \r\n        \r\n        stmt.setInt( 1, addr.getKey() );\r\n        stmt.setString( 2, addr.getFirstName() );\r\n        stmt.setString( 3, addr.getLastName());\r\n        stmt.setString( 4, addr.getNickname());\r\n        stmt.setString( 5, addr.getEmail());\r\n        stmt.setString( 6, addr.getScreenName() );\r\n        stmt.setString( 7, addr.getWorkPhone() );\r\n        stmt.setString( 8, addr.getHomePhone() );\r\n        stmt.setString( 9, addr.getFax() );\r\n        stmt.setString( 10, addr.getPager() );\r\n        stmt.setString( 11, addr.getStreetAddress() );\r\n        stmt.setString( 12, addr.getCity() );\r\n        stmt.setString( 13, addr.getState() );\r\n        stmt.setString( 14, addr.getZip() );\r\n        stmt.setString( 15, addr.getCountry() );\r\n        stmt.setString( 16, addr.getCompany() );\r\n        stmt.setString( 17, addr.getWorkStreetAddress() );\r\n        stmt.setString( 18, addr.getWorkCity() );\r\n        stmt.setString( 19, addr.getWorkState() );\r\n        stmt.setString( 20, addr.getWorkZip() );\r\n        stmt.setString( 21, addr.getWorkCountry() );\r\n        stmt.setString( 22, addr.getWebPage() );\r\n        stmt.setString( 23, addr.getNotes() );\r\n        java.util.Date bd = addr.getBirthday();        \r\n        if( bd != null )\r\n            stmt.setDate( 24, new java.sql.Date( bd.getTime()) );\r\n        else\r\n            stmt.setDate(24, null );\r\n        stmt.setString( 25, addr.getCellPhone());        \r\n        stmt.setString( 26, addr.getVcard());        \r\n        stmt.executeUpdate();\r\n        stmt.close();\r\n\r\n        writeCache( addr );\r\n\r\n    }\r\n    \r\n    /* (non-Javadoc)\r\n     * @see net.sf.borg.model.db.EntityDB#delete(int)\r\n     */\r\n    @Override\r\n    public void delete(int key) throws Exception\r\n    {\r\n        PreparedStatement stmt = JdbcDB.getConnection().prepareStatement( \"DELETE FROM addresses WHERE address_num = ?\" );\r\n        stmt.setInt( 1, key );\r\n        stmt.executeUpdate();\r\n        stmt.close();\r\n\r\n        delCache( key );\r\n    }\r\n    \r\n    /**\r\n     * Gets the keys.\r\n     * \r\n     * @return the keys\r\n     * \r\n     * @throws Exception the exception\r\n     */\r\n    public Collection<Integer> getKeys() throws Exception\r\n    {\r\n        ArrayList<Integer> keys = new ArrayList<Integer>();\r\n        PreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT address_num FROM addresses ORDER BY last_name, first_name\" );\r\n        ResultSet rs = stmt.executeQuery();\r\n        while( rs.next() )\r\n        {\r\n            keys.add( Integer.valueOf(rs.getInt(\"address_num\")) );\r\n        }\r\n        rs.close();\r\n        \r\n        stmt.close();\r\n\r\n        return( keys );\r\n        \r\n    }\r\n    \r\n    /* (non-Javadoc)\r\n     * @see net.sf.borg.model.db.EntityDB#nextkey()\r\n     */\r\n    @Override\r\n    public int nextkey() throws Exception\r\n    {\r\n     PreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT MAX(address_num) FROM addresses\" );\r\n        ResultSet r = stmt.executeQuery();\r\n        int maxKey = 0;\r\n        if( r.next() )\r\n            maxKey = r.getInt(1);\r\n        r.close();\r\n        stmt.close();\r\n\r\n        return ++maxKey;\r\n    }\r\n    \r\n    /* (non-Javadoc)\r\n     * @see net.sf.borg.model.db.EntityDB#newObj()\r\n     */\r\n    @Override\r\n    public Address newObj()\r\n    {\r\n        return( new Address() );\r\n    }\r\n    \r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSOne(int)\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSOne(int key) throws SQLException\r\n\t{\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM addresses WHERE address_num = ?\" );\r\n\t\tstmt.setInt( 1, key );\r\n\t\treturn stmt;\r\n\t}\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSAll()\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSAll() throws SQLException\r\n\t{\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM addresses\" );\r\n\t\treturn stmt;\r\n\t}\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#createFrom(java.sql.ResultSet)\r\n\t */\r\n\t@Override\r\n\tAddress createFrom(ResultSet r) throws SQLException\r\n\t{\r\n\t\tAddress addr = new Address();\r\n\t\taddr.setKey(r.getInt(\"address_num\"));\r\n\t\taddr.setFirstName( r.getString(\"first_name\"));\r\n\t\taddr.setLastName( r.getString(\"last_name\"));\r\n\t\taddr.setNickname( r.getString(\"nickname\"));\r\n\t\taddr.setEmail( r.getString(\"email\"));\r\n\t\taddr.setScreenName( r.getString(\"screen_name\"));\r\n\t\taddr.setWorkPhone( r.getString(\"work_phone\"));\r\n\t\taddr.setHomePhone( r.getString(\"home_phone\"));\r\n\t\taddr.setFax( r.getString(\"fax\"));\r\n\t\taddr.setPager( r.getString(\"pager\"));\r\n\t\taddr.setStreetAddress( r.getString(\"street\"));\r\n\t\taddr.setCity( r.getString(\"city\"));\r\n\t\taddr.setState( r.getString(\"state\"));\r\n\t\taddr.setZip( r.getString(\"zip\"));\r\n\t\taddr.setCountry( r.getString(\"country\"));\r\n\t\taddr.setCompany( r.getString(\"company\"));\r\n\t\taddr.setWorkStreetAddress( r.getString(\"work_street\"));\r\n\t\taddr.setWorkCity( r.getString(\"work_city\"));\r\n\t\taddr.setWorkState( r.getString(\"work_state\"));\r\n\t\taddr.setWorkZip( r.getString(\"work_zip\"));\r\n\t\taddr.setWorkCountry( r.getString(\"work_country\"));\r\n\t\taddr.setWebPage( r.getString(\"webpage\"));\r\n\t\taddr.setNotes( r.getString(\"notes\"));\r\n\t\tif( r.getDate(\"birthday\") != null )\r\n\t\t\taddr.setBirthday( new java.util.Date( r.getDate(\"birthday\").getTime()));\r\n\t\taddr.setCellPhone(r.getString(\"cell_phone\"));\t\t\r\n\t\taddr.setVcard(r.getString(\"vcard\"));\t\t\r\n\t\treturn addr;\r\n\t}\r\n\t\r\n    /* (non-Javadoc)\r\n     * @see net.sf.borg.model.db.EntityDB#updateObj(net.sf.borg.model.entity.KeyedEntity)\r\n     */\r\n    @Override\r\n    public void updateObj(Address addr) throws Exception\r\n    {\r\n   \r\n        PreparedStatement stmt = JdbcDB.getConnection().prepareStatement( \"UPDATE addresses SET \" +\r\n        \"first_name = ?, last_name = ?, nickname = ?, email = ?, screen_name = ?, work_phone = ?,\" + \r\n        \"home_phone = ?, fax = ?, pager = ?, street = ?, city = ?, state = ?, zip = ?, country = ?, company = ?,\" +\r\n        \"work_street = ?, work_city = ?, work_state = ?, work_zip = ?, work_country = ?, webpage = ?, notes = ?, birthday = ?, cell_phone = ?, vcard = ? \" +\r\n        \" WHERE address_num = ?\" );\r\n        \r\n \r\n        stmt.setString( 1, addr.getFirstName() );\r\n        stmt.setString( 2, addr.getLastName());\r\n        stmt.setString( 3, addr.getNickname());\r\n        stmt.setString( 4, addr.getEmail());\r\n        stmt.setString( 5, addr.getScreenName() );\r\n        stmt.setString( 6, addr.getWorkPhone() );\r\n        stmt.setString( 7, addr.getHomePhone() );\r\n        stmt.setString( 8, addr.getFax() );\r\n        stmt.setString( 9, addr.getPager() );\r\n        stmt.setString( 10, addr.getStreetAddress() );\r\n        stmt.setString( 11, addr.getCity() );\r\n        stmt.setString( 12, addr.getState() );\r\n        stmt.setString( 13, addr.getZip() );\r\n        stmt.setString( 14, addr.getCountry() );\r\n        stmt.setString( 15, addr.getCompany() );\r\n        stmt.setString( 16, addr.getWorkStreetAddress() );\r\n        stmt.setString( 17, addr.getWorkCity() );\r\n        stmt.setString( 18, addr.getWorkState() );\r\n        stmt.setString( 19, addr.getWorkZip() );\r\n        stmt.setString( 20, addr.getWorkCountry() );\r\n        stmt.setString( 21, addr.getWebPage() );\r\n        stmt.setString( 22, addr.getNotes() );\r\n        java.util.Date bd = addr.getBirthday();        \r\n        if( bd != null )\r\n            stmt.setDate( 23, new java.sql.Date( bd.getTime()) );\r\n        else\r\n            stmt.setDate(23, null );\r\n\r\n        stmt.setString( 24, addr.getCellPhone() );\r\n        stmt.setString( 25, addr.getVcard() );\r\n        stmt.setInt( 26, addr.getKey() );\r\n        \r\n\r\n        stmt.executeUpdate();\r\n        stmt.close();\r\n\r\n        delCache( addr.getKey() );\r\n        writeCache( addr );\r\n    }\r\n    \r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/ApptJdbcDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n/*\r\n * AppJdbcDB.java\r\n *\r\n * Created on February 1, 2004, 3:59 PM\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.model.db.AppointmentDB;\r\nimport net.sf.borg.model.entity.Appointment;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Calendar;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\n/**\r\n * this is the JDBC layer for access to the appointment table.\r\n */\r\nclass ApptJdbcDB extends JdbcBeanDB<Appointment> implements AppointmentDB {\r\n\r\n\t/**\r\n\t * Creates a new instance of AppJdbcDB.\r\n\t */\r\n\tpublic ApptJdbcDB() {\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.EntityDB#addObj(net.sf.borg.model.entity.KeyedEntity\r\n\t * )\r\n\t */\r\n\t@Override\r\n\tpublic void addObj(Appointment appt) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB\r\n\t\t\t\t.getConnection()\r\n\t\t\t\t.prepareStatement(\r\n\t\t\t\t\t\t\"INSERT INTO appointments (appt_date, appt_num, duration, text, skip_list,\"\r\n\t\t\t\t\t\t\t\t+ \" next_todo, vacation, holiday, private, times, frequency, todo, color, rpt, category, reminders, untimed, encrypted, repeat_until, priority, create_time, lastmod, uid, url ) VALUES \"\r\n\t\t\t\t\t\t\t\t+ \"( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setTimestamp(1, new java.sql.Timestamp(appt.getDate().getTime()),\r\n\t\t\t\tCalendar.getInstance());\r\n\t\tstmt.setInt(2, appt.getKey());\r\n\r\n\t\tstmt.setInt(3, JdbcDB.toInt(appt.getDuration()));\r\n\t\tstmt.setString(4, appt.getText());\r\n\t\tstmt.setString(5, JdbcDB.toStr(appt.getSkipList()));\r\n\t\tjava.util.Date nt = appt.getNextTodo();\r\n\t\tif (nt != null)\r\n\t\t\tstmt.setDate(6, new java.sql.Date(appt.getNextTodo().getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(6, null);\r\n\t\tstmt.setInt(7, JdbcDB.toInt(appt.getVacation()));\r\n\t\tstmt.setInt(8, JdbcDB.toInt(appt.getHoliday()));\r\n\t\tstmt.setInt(9, JdbcDB.toInt(appt.isPrivate()));\r\n\t\tstmt.setInt(10, JdbcDB.toInt(appt.getTimes()));\r\n\t\tstmt.setString(11, appt.getFrequency());\r\n\t\tstmt.setInt(12, JdbcDB.toInt(appt.isTodo()));\r\n\t\tstmt.setString(13, appt.getColor());\r\n\t\tstmt.setInt(14, JdbcDB.toInt(appt.isRepeatFlag()));\r\n\t\tstmt.setString(15, appt.getCategory());\r\n\r\n\t\tstmt.setString(16, appt.getReminderTimes());\r\n\t\tstmt.setString(17, appt.getUntimed());\r\n\t\tif (appt.isEncrypted())\r\n\t\t\tstmt.setString(18, \"Y\");\r\n\t\telse\r\n\t\t\tstmt.setString(18, \"N\");\r\n\t\tjava.util.Date until = appt.getRepeatUntil();\r\n\t\tif (until != null)\r\n\t\t\tstmt.setDate(19, new java.sql.Date(until.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(19, null);\r\n\t\tif (appt.getPriority() != null)\r\n\t\t\tstmt.setInt(20, appt.getPriority().intValue());\r\n\t\telse\r\n\t\t\tstmt.setInt(20, 5);\r\n\t\tstmt.setTimestamp(21, new java.sql.Timestamp(appt.getCreateTime()\r\n\t\t\t\t.getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(22, new java.sql.Timestamp(appt.getLastMod()\r\n\t\t\t\t.getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(23, appt.getUid());\r\n\t\tstmt.setString(24, appt.getUrl());\r\n\t\tstmt.executeUpdate();\r\n\r\n\t\twriteCache(appt);\r\n\r\n\t\tstmt.close();\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#delete(int)\r\n\t */\r\n\t@Override\r\n\tpublic void delete(int key) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"DELETE FROM appointments WHERE appt_num = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\tstmt.executeUpdate();\r\n\r\n\t\tdelCache(key);\r\n\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the keys.\r\n\t * \r\n\t * @return the keys\r\n\t * \r\n\t * @throws Exception\r\n\t *             the exception\r\n\t */\r\n\tpublic static Collection<Integer> getKeys() throws Exception {\r\n\t\tArrayList<Integer> keys = new ArrayList<Integer>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT appt_num FROM appointments\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(Integer.valueOf(rs.getInt(\"appt_num\")));\r\n\t\t}\r\n\r\n\t\trs.close();\r\n\t\tstmt.close();\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.AppointmentDB#getTodoKeys()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Integer> getTodoKeys() throws Exception {\r\n\t\tArrayList<Integer> keys = new ArrayList<Integer>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT appt_num FROM appointments WHERE todo = '1'\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(Integer.valueOf(rs.getInt(\"appt_num\")));\r\n\t\t}\r\n\t\trs.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.AppointmentDB#getRepeatKeys()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Integer> getRepeatKeys() throws Exception {\r\n\t\tArrayList<Integer> keys = new ArrayList<Integer>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT appt_num FROM appointments WHERE rpt = '1'\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(Integer.valueOf(rs.getInt(\"appt_num\")));\r\n\t\t}\r\n\r\n\t\trs.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#nextkey()\r\n\t */\r\n\t@Override\r\n\tpublic int nextkey() throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT MAX(appt_num) FROM appointments\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#newObj()\r\n\t */\r\n\t@Override\r\n\tpublic Appointment newObj() {\r\n\t\treturn (new Appointment());\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSOne(int)\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSOne(int key) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT * FROM appointments WHERE appt_num = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSAll()\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSAll() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT * FROM appointments\");\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#createFrom(java.sql.ResultSet)\r\n\t */\r\n\t@Override\r\n\tAppointment createFrom(ResultSet r) throws SQLException {\r\n\t\tAppointment appt = new Appointment();\r\n\t\tappt.setKey(r.getInt(\"appt_num\"));\r\n\t\tif (r.getTimestamp(\"appt_date\") != null)\r\n\t\t\tappt.setDate(new java.util.Date(r.getTimestamp(\"appt_date\")\r\n\t\t\t\t\t.getTime()));\r\n\t\tappt.setDuration(Integer.valueOf(r.getInt(\"duration\")));\r\n\t\tappt.setText(r.getString(\"text\"));\r\n\t\tappt.setSkipList(JdbcDB.toVect(r.getString(\"skip_list\")));\r\n\t\tif (r.getDate(\"next_todo\") != null)\r\n\t\t\tappt.setNextTodo(new java.util.Date(r.getDate(\"next_todo\")\r\n\t\t\t\t\t.getTime()));\r\n\t\tappt.setVacation(Integer.valueOf(r.getInt(\"vacation\")));\r\n\t\tappt.setHoliday(Integer.valueOf(r.getInt(\"holiday\")));\r\n\t\tappt.setPrivate(r.getInt(\"private\") != 0);\r\n\t\tappt.setTimes(Integer.valueOf(r.getInt(\"times\")));\r\n\t\tappt.setFrequency(r.getString(\"frequency\"));\r\n\t\tappt.setTodo(r.getInt(\"todo\") != 0);\r\n\t\tappt.setColor(r.getString(\"color\"));\r\n\t\tappt.setRepeatFlag(r.getInt(\"rpt\") != 0);\r\n\t\tappt.setCategory(r.getString(\"category\"));\r\n\t\tappt.setReminderTimes(r.getString(\"reminders\"));\r\n\t\tappt.setUntimed(r.getString(\"untimed\"));\r\n\t\tString enc = r.getString(\"encrypted\");\r\n\t\tif (enc != null && enc.equals(\"Y\"))\r\n\t\t\tappt.setEncrypted(true);\r\n\t\tif (r.getDate(\"repeat_until\") != null)\r\n\t\t\tappt.setRepeatUntil(new java.util.Date(r.getDate(\"repeat_until\")\r\n\t\t\t\t\t.getTime()));\r\n\t\tappt.setPriority(Integer.valueOf(r.getInt(\"priority\")));\r\n\t\tif (r.getTimestamp(\"create_time\") != null)\r\n\t\t\tappt.setCreateTime(new java.util.Date(r.getTimestamp(\"create_time\")\r\n\t\t\t\t\t.getTime()));\r\n\t\tif (r.getTimestamp(\"lastmod\") != null)\r\n\t\t\tappt.setLastMod(new java.util.Date(r.getTimestamp(\"lastmod\")\r\n\t\t\t\t\t.getTime()));\r\n\t\tappt.setUid(r.getString(\"uid\"));\r\n\t\tappt.setUrl(r.getString(\"url\"));\r\n\t\treturn appt;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.EntityDB#updateObj(net.sf.borg.model.entity.KeyedEntity\r\n\t * )\r\n\t */\r\n\t@Override\r\n\tpublic void updateObj(Appointment appt) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB\r\n\t\t\t\t.getConnection()\r\n\t\t\t\t.prepareStatement(\r\n\t\t\t\t\t\t\"UPDATE appointments SET  appt_date = ?, \"\r\n\t\t\t\t\t\t\t\t+ \"duration = ?, text = ?, skip_list = ?,\"\r\n\t\t\t\t\t\t\t\t+ \" next_todo = ?, vacation = ?, holiday = ?, private = ?, times = ?, frequency = ?, todo = ?, color = ?, rpt = ?, category = ?,\"\r\n\t\t\t\t\t\t\t\t+ \" reminders = ?, untimed = ?, encrypted = ?, repeat_until = ?, priority = ?, create_time = ?, lastmod = ?, uid = ?, url = ?\"\r\n\t\t\t\t\t\t\t\t+ \" WHERE appt_num = ?\");\r\n\r\n\t\tstmt.setTimestamp(1, new java.sql.Timestamp(appt.getDate().getTime()),\r\n\t\t\t\tCalendar.getInstance());\r\n\r\n\t\tstmt.setInt(2, JdbcDB.toInt(appt.getDuration()));\r\n\t\tstmt.setString(3, appt.getText());\r\n\t\tstmt.setString(4, JdbcDB.toStr(appt.getSkipList()));\r\n\t\tjava.util.Date nt = appt.getNextTodo();\r\n\t\tif (nt != null)\r\n\t\t\tstmt.setDate(5, new java.sql.Date(appt.getNextTodo().getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(5, null);\r\n\t\tstmt.setInt(6, JdbcDB.toInt(appt.getVacation()));\r\n\t\tstmt.setInt(7, JdbcDB.toInt(appt.getHoliday()));\r\n\t\tstmt.setInt(8, JdbcDB.toInt(appt.isPrivate()));\r\n\t\tstmt.setInt(9, JdbcDB.toInt(appt.getTimes()));\r\n\t\tstmt.setString(10, appt.getFrequency());\r\n\t\tstmt.setInt(11, JdbcDB.toInt(appt.isTodo()));\r\n\t\tstmt.setString(12, appt.getColor());\r\n\t\tstmt.setInt(13, JdbcDB.toInt(appt.isRepeatFlag()));\r\n\t\tstmt.setString(14, appt.getCategory());\r\n\t\tstmt.setString(15, appt.getReminderTimes());\r\n\t\tstmt.setString(16, appt.getUntimed());\r\n\t\tif (appt.isEncrypted())\r\n\t\t\tstmt.setString(17, \"Y\");\r\n\t\telse\r\n\t\t\tstmt.setString(17, \"N\");\r\n\t\tjava.util.Date until = appt.getRepeatUntil();\r\n\t\tif (until != null)\r\n\t\t\tstmt.setDate(18, new java.sql.Date(until.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(18, null);\r\n\t\tif (appt.getPriority() != null)\r\n\t\t\tstmt.setInt(19, appt.getPriority().intValue());\r\n\t\telse\r\n\t\t\tstmt.setInt(19, 5);\r\n\r\n\t\tstmt.setTimestamp(20, new java.sql.Timestamp(appt.getCreateTime()\r\n\t\t\t\t.getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(21, new java.sql.Timestamp(appt.getLastMod()\r\n\t\t\t\t.getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(22, appt.getUid());\r\n\t\tstmt.setString(23, appt.getUrl());\r\n\t\tstmt.setInt(24, appt.getKey());\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t\tdelCache(appt.getKey());\r\n\t\twriteCache(appt);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic List<Appointment> getAppointmentsByText(String text)\r\n\t\t\tthrows Exception {\r\n\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT * FROM appointments where text = ?\");\r\n\t\tstmt.setString(1, text);\r\n\t\tList<Appointment> lst = new ArrayList<Appointment>();\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tAppointment bean = createFrom(r);\r\n\t\t\t\tlst.add(bean);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Appointment getAppointmentByUid(String uid) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\r\n\t\t\t\t\"SELECT * FROM appointments where uid = ?\");\r\n\t\tstmt.setString(1, uid);\r\n\t\tAppointment ap = null;\r\n\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tap = createFrom(r);\r\n\t\t\t}\r\n\t\t\treturn ap;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/CheckListJdbcDB.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport jakarta.xml.bind.JAXBContext;\r\nimport jakarta.xml.bind.JAXBException;\r\nimport jakarta.xml.bind.Marshaller;\r\nimport jakarta.xml.bind.Unmarshaller;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport net.sf.borg.model.db.CheckListDB;\r\nimport net.sf.borg.model.entity.CheckList;\r\n\r\nimport java.io.StringReader;\r\nimport java.io.StringWriter;\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\n/**\r\n * provides the JDBC layer for reading/writing CheckLists.\r\n */\r\nclass CheckListJdbcDB implements CheckListDB {\r\n\r\n\tpublic CheckListJdbcDB() {\r\n\t\t\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.CheckListDB#addCheckList(net.sf.borg.model.entity\r\n\t * .CheckList)\r\n\t */\r\n\t@Override\r\n\tpublic void addCheckList(CheckList m) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO checkLists ( name, text ) \"\r\n\t\t\t\t\t\t+ \" VALUES \" + \"( ?, ? )\");\r\n\r\n\t\tstmt.setString(1, m.getCheckListName());\r\n\t\tstmt.setString(2, getItemsXml(m));\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.CheckListDB#delete(java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic void delete(String name) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"DELETE FROM checkLists WHERE name = ?\");\r\n\t\tstmt.setString(1, name);\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.CheckListDB#getNames()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<String> getNames() throws Exception {\r\n\t\tArrayList<String> keys = new ArrayList<String>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT name FROM checkLists ORDER BY name\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(rs.getString(\"name\"));\r\n\t\t}\r\n\t\trs.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\tprivate PreparedStatement getPSOne(String name) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * FROM checkLists WHERE name = ?\");\r\n\t\tstmt.setString(1, name);\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\tprivate PreparedStatement getPSAll() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * FROM checkLists\");\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/**\r\n\t * create a CheckList from a result set\r\n\t * \r\n\t * @param r\r\n\t *            the result set\r\n\t * @return the CheckList object\r\n\t * @throws SQLException\r\n\t */\r\n\tprivate CheckList createFrom(ResultSet r) throws Exception {\r\n\t\tCheckList m = new CheckList();\r\n\r\n\t\tm.setCheckListName(r.getString(\"name\"));\r\n\t\tsetItemsFromXml(m,r.getString(\"text\"));\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.CheckListDB#readAll()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<CheckList> readAll() throws Exception {\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tstmt = getPSAll();\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tList<CheckList> lst = new ArrayList<CheckList>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tCheckList bean = createFrom(r);\r\n\t\t\t\tlst.add(bean);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.CheckListDB#readCheckList(java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic CheckList readCheckList(String name) throws Exception {\r\n\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tCheckList m = null;\r\n\t\t\tstmt = getPSOne(name);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tm = createFrom(r);\r\n\t\t\t}\r\n\t\t\treturn m;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.CheckListDB#updateCheckList(net.sf.borg.model.entity\r\n\t * .CheckList)\r\n\t */\r\n\t@Override\r\n\tpublic void updateCheckList(CheckList m) throws Exception {\r\n\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"UPDATE checkLists SET \"\r\n\t\t\t\t\t\t+ \" text = ? WHERE name = ?\");\r\n\r\n\t\tstmt.setString(1, getItemsXml(m));\r\n\t\tstmt.setString(2, m.getCheckListName());\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t@XmlRootElement(name = \"ITEMS\")\r\n\tprivate static class XmlContainer {\r\n\t\tpublic Collection<CheckList.Item> items;\r\n\t}\r\n\r\n\t/**\r\n\t * get the net.sf.borg.ui.checklist items from a net.sf.borg.ui.checklist in XML for peristing\r\n\t * \r\n\t * @param cl\r\n\t * @return items XML string\r\n\t * @throws JAXBException\r\n\t */\r\n\tprivate static String getItemsXml(CheckList cl) throws JAXBException {\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tMarshaller m = jc.createMarshaller();\r\n\t\tXmlContainer container = new XmlContainer();\r\n\t\tcontainer.items = cl.getItems();\r\n\t\tStringWriter sw = new StringWriter();\r\n\t\tm.marshal(container, sw);\r\n\t\treturn sw.toString();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * set the items in a net.sf.borg.ui.checklist from XML\r\n\t * \r\n\t * @param cl\r\n\t *            - the net.sf.borg.ui.checklist\r\n\t * @param itemXml\r\n\t *            - the XML\r\n\t * @throws JAXBException \r\n\t */\r\n\tprivate static void setItemsFromXml(CheckList cl, String itemXml) throws JAXBException {\r\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\r\n\t\tUnmarshaller u = jc.createUnmarshaller();\r\n\r\n\t\tXmlContainer container = (XmlContainer) u.unmarshal(new StringReader(\r\n\t\t\t\titemXml));\r\n\t\t\r\n\t\tcl.getItems().clear();\r\n\t\tif( container.items != null )\r\n\t\t\tcl.getItems().addAll(container.items);\r\n\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/DbDirtyManager.java",
    "content": "// BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/DbDirtyManager.java - hy copilot\n\npackage net.sf.borg.model.db.jdbc;\n\nimport java.sql.Connection;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Logger;\n\n/**\n * Manages the dirty flag for the SQLite database using SQLite's update hook.\n * The update hook is triggered whenever INSERT, UPDATE, or DELETE occurs.\n */\npublic class DbDirtyManager {\n\t\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\t\n\tstatic private DbDirtyManager singleton = null;\n\t\n\t/**\n\t * Listener interface for dirty database notifications\n\t */\n\tpublic interface DbDirtyListener {\n\t\t/**\n\t\t * Called when the database becomes dirty (has been modified)\n\t\t */\n\t\tvoid onDbDirty();\n\t\t\n\t\t/**\n\t\t * Called when the database is cleaned (uploaded/synced)\n\t\t */\n\t\tvoid onDbClean();\n\t}\n\t\n\tprivate volatile boolean dbDirty = false;\n\tprivate volatile boolean enabled = true;\n\t\n\tpublic boolean isEnabled() {\n\t\treturn enabled;\n\t}\n\n\tpublic void setEnabled(boolean enabled) {\n\t\tthis.enabled = enabled;\n\t}\n\n\tprivate final List<DbDirtyListener> listeners = new ArrayList<>();\n\t\n\t/**\n\t * Get the singleton instance\n\t */\n\tstatic public DbDirtyManager getReference() {\n\t\tif (singleton == null) {\n\t\t\tsingleton = new DbDirtyManager();\n\t\t}\n\t\treturn singleton;\n\t}\n\t\n\tprivate DbDirtyManager() {\n\t}\n\t\n\t\n\t/**\n\t * Initialize the SQLite update hook on the given connection.\n\t * This must be called after the SQLite connection is established.\n\t * \n\t * @param connection the SQLite connection\n\t * @throws Exception if hook setup fails\n\t */\n\tpublic void initUpdateHook(Connection connection) throws Exception {\n\t\tif (connection == null) {\n\t\t\tthrow new Exception(\"Connection cannot be null\");\n\t\t}\n\t\t\n\t\ttry {\n\t\t\t// SQLite JDBC driver provides access to the native SQLite update hook\n\t\t\t// through a custom interface. Use the connection's underlying SQLite connection.\n\t\t\torg.sqlite.SQLiteConnection sqliteConn = connection.unwrap(org.sqlite.SQLiteConnection.class);\n\t\t\n\t\t\t\n\t\t\tsqliteConn.addUpdateListener((type, db, table, rowId) -> {\n\t\t\t\tlog.fine(\"SQLite update hook fired: type=\" + type + \" database=\" + db + \" table=\" + table + \" rowid=\" + rowId);\n\t\t\t\tsetDirty();\n\t\t\t});\n\t\t\t\n\t\t\tlog.info(\"SQLite update hook initialized\");\n\t\t} catch (Exception e) {\n\t\t\tlog.warning(\"Failed to initialize SQLite update hook: \" + e.getMessage());\n\t\t\tthrow e;\n\t\t}\n\t}\n\t\n\t/**\n\t * Check if the database has pending updates\n\t * \n\t * @return true if database is dirty, false otherwise\n\t */\n\tpublic boolean isDirty() {\n\t\treturn dbDirty;\n\t}\n\t\n\t/**\n\t * Mark the database as dirty (has been modified since last upload).\n\t * Automatically called by the SQLite update hook, but can also be called manually.\n\t */\n\tpublic void setDirty() {\n\t\tif (!dbDirty && enabled) {\n\t\t\tlog.fine(\"Database marked as dirty\");\n\t\t\tdbDirty = true;\n\t\t\tnotifyDirtyListeners();\n\t\t}\n\t}\n\t\n\t/**\n\t * Mark the database as clean (successfully uploaded).\n\t * Call this after successful database upload to Google Drive.\n\t */\n\tpublic void setClean() {\n\t\tif (dbDirty) {\n\t\t\tlog.fine(\"Database marked as clean\");\n\t\t\tdbDirty = false;\n\t\t\tnotifyCleanListeners();\n\t\t}\n\t}\n\t\n\t/**\n\t * Add a listener to be notified of dirty/clean state changes\n\t * \n\t * @param listener the listener to add\n\t */\n\tpublic void addListener(DbDirtyListener listener) {\n\t\tsynchronized (listeners) {\n\t\t\tlisteners.add(listener);\n\t\t}\n\t}\n\t\n\t/**\n\t * Remove a listener\n\t * \n\t * @param listener the listener to remove\n\t */\n\tpublic void removeListener(DbDirtyListener listener) {\n\t\tsynchronized (listeners) {\n\t\t\tlisteners.remove(listener);\n\t\t}\n\t}\n\t\n\tprivate void notifyDirtyListeners() {\n\t\tsynchronized (listeners) {\n\t\t\tfor (DbDirtyListener listener : listeners) {\n\t\t\t\tlistener.onDbDirty();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void notifyCleanListeners() {\n\t\tsynchronized (listeners) {\n\t\t\tfor (DbDirtyListener listener : listeners) {\n\t\t\t\tlistener.onDbClean();\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/JdbcBeanDB.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * JdbcDB.java\r\n *\r\n * Created on February 2, 2004, 12:57 PM\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.model.entity.KeyedEntity;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.HashMap;\r\n\r\n\r\n/**\r\n * abstract base class for all classes that manage reading/writing of KeyedEntities\r\n * to and from a JDBC database. also provides an object cache.\r\n */\r\nabstract class JdbcBeanDB<T extends KeyedEntity<T>> {\r\n\r\n\t// BORG needs its own caching. BORG rebuilds the map of DB data often\r\n\t// and going to the DB is too expensive. If BORG is changed to support\r\n\t// multi-user access, then the cache algorithm will have to be smarter.\r\n\t// currently, it is assumed that a single BORG client is the only DB\r\n\t// updater.\r\n\t// in the future, if there are multiple writers, the DB will have to\r\n\t// contain\r\n\t// some info to indicate when another process has written the DB to\r\n\t// force\r\n\t// a flush of the cache\r\n\tprivate final boolean objectCacheOn_; // is caching on?\r\n\r\n\tprivate final HashMap<Integer,T> objectCache_; // the cache\r\n\r\n\t/**\r\n\t * Creates a new instance of JdbcDB.\r\n\t */\r\n\tJdbcBeanDB()  {\r\n\t\tobjectCacheOn_ = true;\r\n\t\tobjectCache_ = new HashMap<Integer,T>();\r\n\t}\r\n\r\n\t/**\r\n\t * flush the cache\r\n\t */\r\n\tpublic void sync() {\r\n\t\temptyCache();\r\n\t}\r\n\r\n\tprotected void writeCache(T bean) {\r\n\t\t// put a copy of the bean in the cache\r\n\t\tif (objectCacheOn_) {\r\n\t\t\tobjectCache_.put(Integer.valueOf(bean.getKey()), bean.copy());\r\n\t\t}\r\n\t}\r\n\r\n\tprotected void emptyCache() {\r\n\t\tif (objectCacheOn_)\r\n\t\t\tobjectCache_.clear();\r\n\t}\r\n\r\n\tprotected void delCache(int key) {\r\n\t\t// remove the bean from the cache\r\n\t\tif (objectCacheOn_) {\r\n\t\t\tobjectCache_.remove(Integer.valueOf(key));\r\n\t\t}\r\n\t}\r\n\r\n\tprotected T readCache(int key) {\r\n\t\t// need to remove cache here if DB has been updated\r\n\t\t// by any other process besides this one\r\n\t\t// TBD\r\n\r\n\t\t// if the bean is in the cache - return it\r\n\t\tif (objectCacheOn_) {\r\n\t\t\tT o = objectCache_.get(Integer.valueOf(key));\r\n\r\n\t\t\tif (o != null) {\r\n\t\t\t\treturn o.copy();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn (null);\r\n\t}\r\n\r\n\t/**\r\n\t * Read all Entities.\r\n\t * \r\n\t * @return A collection of all Entities in the DB table\r\n\t * \r\n\t * @throws Exception \r\n\t */\r\n\tpublic Collection<T> readAll() throws Exception {\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tstmt = getPSAll();\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tCollection<T> lst = new ArrayList<T>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tT bean = createFrom(r);\r\n\t\t\t\tlst.add(bean);\r\n\t\t\t\twriteCache(bean);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Read a single KeyedEntity from the database by key\r\n\t * \r\n\t * @param key the key\r\n\t * \r\n\t * @return the entity\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic T readObj(int key) throws Exception {\r\n\t\tT bean = readCache(key);\r\n\r\n\t\tif (bean != null)\r\n\t\t\treturn bean;\r\n\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tstmt = getPSOne(key);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tbean = createFrom(r);\r\n\t\t\t\twriteCache(bean);\r\n\t\t\t}\r\n\t\t\treturn bean;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * @return a PreparedStatement that reads one entity by key\r\n\t * @throws SQLException\r\n\t */\r\n\tabstract PreparedStatement getPSOne(int key) throws SQLException;\r\n\t\r\n\t/**\r\n\t * \r\n\t * @return a PreparedStatement that reads all entities from a table\r\n\t * @throws SQLException\r\n\t */\r\n\tabstract PreparedStatement getPSAll() throws SQLException;\r\n\r\n\t/**\r\n\t * Fills in an entity from a result set\r\n\t * @param rs the result set\r\n\t * @return the entity\r\n\t * @throws SQLException\r\n\t */\r\n\tabstract T createFrom(ResultSet rs) throws SQLException;\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/JdbcDB.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * JdbcDB.java\r\n *\r\n * Created on February 2, 2004, 12:57 PM\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport java.io.File;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.sql.Connection;\r\nimport java.sql.DriverManager;\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.Date;\r\nimport java.util.Properties;\r\nimport java.util.StringTokenizer;\r\nimport java.util.Vector;\r\nimport java.util.logging.Logger;\r\n\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.PrefName;\r\nimport net.sf.borg.common.Prefs;\r\nimport net.sf.borg.model.entity.Option;\r\n\r\n/**\r\n * class providing basic common JDBC services\r\n */\r\nfinal public class JdbcDB {\r\n\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\t// common db connection shared by sub-classes. in BORG, all sub-classes\r\n\t// will manage a table in the same DB\r\n\tstatic protected Connection connection_ = null;\r\n\r\n\t// JDBC URL\r\n\tstatic private String url_;\r\n\r\n\tstatic private String getDriverType() {\r\n\t\tif (url_ != null && url_.startsWith(\"jdbc:\"))\r\n\t\t\treturn url_.substring(5, url_.indexOf(':', 6));\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * begin a JDBC transaction on the shared connection\r\n\t *\r\n\t * @throws Exception\r\n\t */\r\n\tstatic synchronized public void beginTransaction() throws Exception {\r\n\t\tif (\"sqlite\".equals(getDriverType()))\r\n\t\t\treturn;\r\n\t\tconnection_.setAutoCommit(false);\r\n\t}\r\n\r\n\t/**\r\n\t * commit a JDBC transaction on the shared connection\r\n\t *\r\n\t * @throws Exception\r\n\t */\r\n\tstatic synchronized public final void commitTransaction() throws Exception {\r\n\t\tif (\"sqlite\".equals(getDriverType()))\r\n\t\t\treturn;\r\n\t\tPreparedStatement stmt = connection_.prepareStatement(\"COMMIT\");\r\n\t\tstmt.execute();\r\n\t\tstmt.close();\r\n\t\tconnection_.setAutoCommit(true);\r\n\t}\r\n\r\n\t/**\r\n\t * rollback a JDBC transaction on the shared connection\r\n\t *\r\n\t * @throws Exception\r\n\t */\r\n\tstatic synchronized public final void rollbackTransaction() throws Exception {\r\n\t\tif (\"sqlite\".equals(getDriverType()))\r\n\t\t\treturn;\r\n\t\tPreparedStatement stmt = connection_.prepareStatement(\"ROLLBACK\");\r\n\t\tstmt.execute();\r\n\t\tstmt.close();\r\n\t\tconnection_.setAutoCommit(true);\r\n\t}\r\n\r\n\t/**\r\n\t * Connect to the database. The logic varies based on the URL.\r\n\t *\r\n\t * @param urlIn the JDBC url\r\n\t * @throws Exception the exception\r\n\t */\r\n\tstatic synchronized public void connect(String urlIn) throws Exception {\r\n\t\tString url = urlIn;\r\n\t\tif (url == null)\r\n\t\t\turl = buildDbDir();\r\n\t\turl_ = url;\r\n\t\tif (url.startsWith(\"jdbc:h2\")) {\r\n\t\t\tif (connection_ == null) {\r\n\t\t\t\tProperties props = new Properties();\r\n\t\t\t\tprops.setProperty(\"user\", \"sa\");\r\n\t\t\t\tprops.setProperty(\"password\", \"\");\r\n\t\t\t\tprops.setProperty(\"shutdown\", \"true\");\r\n\t\t\t\tprops.setProperty(\"ifexists\", \"true\");\r\n\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconnection_ = DriverManager.getConnection(url, props);\r\n\t\t\t\t} catch (SQLException se) {\r\n\t\t\t\t\tif (se.getSQLState().equals(\"90146\")) {\r\n\t\t\t\t\t\t// need to create the db\r\n\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\tlog.info(\"Creating Database\");\r\n\t\t\t\t\t\t\tInputStream is = JdbcDB.class.getResourceAsStream(\"/borg_hsqldb.sql\");\r\n\t\t\t\t\t\t\tStringBuffer sb = new StringBuffer();\r\n\t\t\t\t\t\t\tInputStreamReader r = new InputStreamReader(is);\r\n\t\t\t\t\t\t\twhile (true) {\r\n\t\t\t\t\t\t\t\tint ch = r.read();\r\n\t\t\t\t\t\t\t\tif (ch == -1)\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\tsb.append((char) ch);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tr.close();\r\n\t\t\t\t\t\t\tprops.setProperty(\"ifexists\", \"false\");\r\n\t\t\t\t\t\t\tconnection_ = DriverManager.getConnection(url, props);\r\n\t\t\t\t\t\t\texecSQL(sb.toString());\r\n\t\t\t\t\t\t} catch (Exception e2) {\r\n\t\t\t\t\t\t\tthrow e2;\r\n\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} else if (se.getMessage().indexOf(\"locked\") != -1) {\r\n\t\t\t\t\t\tthrow se;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tthrow se;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if (url.startsWith(\"jdbc:sqlite\")) {\r\n\t\t\tif (connection_ == null) {\r\n\r\n\t\t\t\ttry {\r\n\t\t\t\t\tProperties props = new Properties();\r\n\t\t\t\t\tprops.setProperty(\"allowMultiQueries\", \"true\");\r\n\t\t\t\t\tconnection_ = DriverManager.getConnection(url, props);\r\n\r\n\t\t\t\t\t// turn on foreign key validation\r\n\t\t\t\t\tString s = \"PRAGMA foreign_keys = ON\";\r\n\t\t\t\t\tlog.fine(\"SQL: \" + s);\r\n\t\t\t\t\texecSQL(s);\r\n\t\t\t\t\t\r\n\t\t\t\t\t// Initialize the update hook for dirty tracking\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tDbDirtyManager.getReference().initUpdateHook(connection_);\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\tlog.warning(\"Update hook initialization failed, dirty flag will not work: \" + e.getMessage());\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tInputStream is = JdbcDB.class.getResourceAsStream(\"/borg_sqlite.sql\");\r\n\t\t\t\t\tStringBuffer sb = new StringBuffer();\r\n\t\t\t\t\tInputStreamReader r = new InputStreamReader(is);\r\n\t\t\t\t\tboolean more = true;\r\n\t\t\t\t\twhile (more) {\r\n\t\t\t\t\t\twhile (true) {\r\n\t\t\t\t\t\t\tint ch = r.read();\r\n\t\t\t\t\t\t\tif (ch == ';') {\r\n\t\t\t\t\t\t\t\tsb.append(\";\\n\");\r\n\t\t\t\t\t\t\t\tlog.fine(\"SQL: \" + sb);\r\n\t\t\t\t\t\t\t\texecSQL(sb.toString());\r\n\t\t\t\t\t\t\t\tsb.setLength(0);\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (ch == -1) {\r\n\t\t\t\t\t\t\t\tmore = false;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tsb.append((char) ch);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tr.close();\r\n\t\t\t\t} catch (SQLException se) {\r\n\t\t\t\t\tlog.severe(se.getMessage());\r\n\t\t\t\t\tse.printStackTrace();\r\n\t\t\t\t\tthrow se;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t} else {\r\n\t\t\tif (connection_ == null) {\r\n\t\t\t\tconnection_ = DriverManager.getConnection(url);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * perform a clean HSQL shutdown if we are using HSQL\r\n\t */\r\n\tstatic synchronized private final void cleanup() {\r\n\t\ttry {\r\n\t\t\tif (connection_ != null && !connection_.isClosed() && url_.startsWith(\"jdbc:hsqldb:file\")) {\r\n\r\n\t\t\t\texecSQL(\"SHUTDOWN\");\r\n\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t//\r\n\t// various data conversion methods are below to provide a standard\r\n\t// db representation of String Lists, Integer, and boolean\r\n\t//\r\n\tpublic final static String toStr(Vector<String> v) {\r\n\t\tStringBuffer val = new StringBuffer();\r\n\t\tif (v == null)\r\n\t\t\treturn (\"\");\r\n\t\ttry {\r\n\t\t\twhile (true) {\r\n\t\t\t\tString s = v.remove(0);\r\n\t\t\t\tval.append(s);\r\n\t\t\t\tval.append(\",\");\r\n\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\t// empty\r\n\t\t}\r\n\t\treturn (val.toString());\r\n\t}\r\n\r\n\tpublic final static int toInt(Integer in) {\r\n\t\tif (in == null)\r\n\t\t\treturn (0);\r\n\t\treturn (in.intValue());\r\n\t}\r\n\r\n\tpublic final static int toInt(boolean in) {\r\n\t\tif (in == false)\r\n\t\t\treturn (0);\r\n\t\treturn (1);\r\n\t}\r\n\r\n\tpublic final static Vector<String> toVect(String s) {\r\n\t\tif (s == null || s.equals(\"\"))\r\n\t\t\treturn (null);\r\n\r\n\t\tStringTokenizer stk = new StringTokenizer(s, \",\");\r\n\t\tVector<String> vect = new Vector<String>();\r\n\t\twhile (stk.hasMoreTokens()) {\r\n\t\t\tString stt = stk.nextToken();\r\n\t\t\tif (!stt.equals(\"\"))\r\n\t\t\t\tvect.add(stt);\r\n\t\t}\r\n\t\treturn (vect);\r\n\t}\r\n\r\n\t/**\r\n\t * Execute arbitrary SQL against the open JDBC connection\r\n\t *\r\n\t * @param sql the sql\r\n\t * @return the result set\r\n\t * @throws Exception the exception\r\n\t */\r\n\tstatic synchronized final public void execSQL(String sql) throws Exception {\r\n\t\tPreparedStatement stmt = connection_.prepareStatement(sql);\r\n\t\tstmt.execute();\r\n\t\tstmt.close();\r\n\t}\r\n\r\n\tstatic synchronized final public ResultSet execQuery(String sql) throws Exception {\r\n\t\tPreparedStatement stmt = connection_.prepareStatement(sql);\r\n\t\tstmt.execute();\r\n\t\treturn stmt.getResultSet();\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the connection.\r\n\t *\r\n\t * @return the connection\r\n\t */\r\n\tstatic synchronized public Connection getConnection() {\r\n\t\treturn connection_;\r\n\t}\r\n\r\n\t/**\r\n\t * Close the open connection and shutdown the db (if HSQL)\r\n\t *\r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic synchronized static void close() throws Exception {\r\n\t\twriteTimestamp();\r\n\t\tcleanup();\r\n\t\tif (connection_ != null)\r\n\t\t\tconnection_.close();\r\n\t\tconnection_ = null;\r\n\t}\r\n\r\n\t/**\r\n\t * write a timestamp to the DB and prefs to detect shutdown problem on next\r\n\t * start\r\n\t *\r\n\t * @throws Exception\r\n\t */\r\n\tprivate static void writeTimestamp() throws Exception {\r\n\t\tDate now = new Date();\r\n\t\tPrefs.putPref(PrefName.SHUTDOWNTIME, Long.toString(now.getTime()));\r\n\t\tOption option = new Option(PrefName.SHUTDOWNTIME.getName(), Long.toString(now.getTime()));\r\n\t\tnew OptionJdbcDB().setOption(option);\r\n\t}\r\n\r\n\t/**\r\n\t * Builds the db url from the user's settings. Supports HSQL, SQLITE, generic\r\n\t * JDBC\r\n\t *\r\n\t * @return the jdbc url\r\n\t */\r\n\tpublic static String buildDbDir() {\r\n\t\t// get dir for DB\r\n\t\tString dbdir = \"\";\r\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\r\n\t\tif (dbtype.equals(\"h2\")) {\r\n\t\t\tString hdir = Prefs.getPref(PrefName.H2DIR);\r\n\t\t\tif (hdir.equals(\"not-set\"))\r\n\t\t\t\treturn hdir;\r\n\t\t\tdbdir = \"jdbc:h2:file:\" + Prefs.getPref(PrefName.H2DIR) + \"/borgdb;USER=sa;NON_KEYWORDS=value\";\r\n\t\t} else if (dbtype.equals(\"sqlite\")) {\r\n\t\t\tString hdir = Prefs.getPref(PrefName.SQLITEDIR);\r\n\t\t\tif (hdir.equals(\"not-set\"))\r\n\t\t\t\treturn hdir;\r\n\r\n\t\t\t// make sure dir exists\r\n\t\t\tFile borgdir = new File(hdir);\r\n\t\t\tif (!borgdir.exists()) {\r\n\t\t\t\tborgdir.mkdir();\r\n\t\t\t}\r\n\t\t\tdbdir = \"jdbc:sqlite:\" + Prefs.getPref(PrefName.SQLITEDIR) + \"/borg_sqlite.db\";\r\n\t\t} else if (dbtype.equals(\"jdbc\")) {\r\n\t\t\tdbdir = Prefs.getPref(PrefName.JDBCURL);\r\n\t\t}\r\n\r\n\t\tlog.info(\"DB URL is: \" + dbdir);\r\n\t\treturn (dbdir);\r\n\t}\r\n\r\n\tpublic static String getDBFilePath() {\r\n\r\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\r\n\t\tif (dbtype.equals(\"h2\")) {\r\n\t\t\treturn Prefs.getPref(PrefName.H2DIR) + \"/borgdb.mv.db\";\r\n\t\t} else if (dbtype.equals(\"sqlite\")) {\r\n\t\t\treturn Prefs.getPref(PrefName.SQLITEDIR) + \"/borg_sqlite.db\";\r\n\t\t} \r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/JdbcDBHelper.java",
    "content": "package net.sf.borg.model.db.jdbc;\n\nimport net.sf.borg.model.db.*;\nimport net.sf.borg.model.entity.Address;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\n\n/**\n * Helper class that provides a db factory and some db control functionality to packages outside of the jdbc package.\n * This is the only functionality from the jdbc package that is visible to other packages\n */\npublic class JdbcDBHelper implements DBHelper.Factory, DBHelper.Controller {\n\n\t@Override\n\tpublic AppointmentDB createAppointmentDB() {\n\t\treturn new ApptJdbcDB();\n\t}\n\n\t@Override\n\tpublic CheckListDB createCheckListDB() {\n\t\treturn new CheckListJdbcDB();\n\t}\n\n\t@Override\n\tpublic LinkDB createLinkDB() {\n\t\treturn new LinkJdbcDB();\n\t}\n\n\t@Override\n\tpublic MemoDB createMemoDB() {\n\t\treturn new MemoJdbcDB();\n\t}\n\n\t@Override\n\tpublic OptionDB createOptionDB() {\n\t\treturn new OptionJdbcDB();\n\t}\n\n\t@Override\n\tpublic TaskDB createTaskDB() {\n\t\treturn new TaskJdbcDB();\n\t}\n\n\t@Override\n\tpublic EntityDB<Address> createAddressDB() {\n\t\treturn new AddrJdbcDB();\n\t}\n\n\t@Override\n\tpublic String buildURL() {\n\t\treturn JdbcDB.buildDbDir();\n\t}\n\n\t@Override\n\tpublic void connect(String url) throws Exception {\n\t\tJdbcDB.connect(url);\n\t}\n\n\t@Override\n\tpublic void close() throws Exception {\n\t\tJdbcDB.close();\n\t}\n\n\t@Override\n\tpublic void execSQL(String string) throws Exception {\n\t\tJdbcDB.execSQL(string);\n\t}\n\t\n\t@Override\n\tpublic ResultSet execQuery(String string) throws Exception {\n\t\treturn JdbcDB.execQuery(string);\n\t}\n\n\t@Override\n\tpublic void beginTransaction() throws Exception {\n\t\tJdbcDB.beginTransaction();\n\t}\n\n\t@Override\n\tpublic void commitTransaction() throws Exception {\n\t\tJdbcDB.commitTransaction();\n\t}\n\n\t@Override\n\tpublic void rollbackTransaction() throws Exception {\n\t\tJdbcDB.rollbackTransaction();\n\t}\n\n\t@Override\n\tpublic Connection getConnection() {\n\t\treturn JdbcDB.getConnection();\n\t}\n\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/JdbcDBUpgrader.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.common.Errmsg;\r\n\r\nimport java.sql.SQLException;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * Class JdbcDBUpgrader is used to upgrade HSQL database in-place when the schema changes for a \r\n * release. This class will check an update condition, and if true, execute SQL to upgrade the DB.\r\n * It is meant to be called during model initialization - preferably from the constructors\r\n * of the JdbcDB classes\r\n */\r\n\r\npublic class JdbcDBUpgrader {\r\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\r\n\r\n\r\n\t/** The check sql. */\r\n\tprivate final String checkSql;\r\n\r\n\t/** The upd sql. */\r\n\tprivate final String[] updSql;\r\n\r\n\t/**\r\n\t * Instantiates a new jdbc db upgrader.\r\n\t * \r\n\t * @param checkSql the sql that checks if an upgrade is needed\r\n\t * @param usql the sql to upgrade the db if needed\r\n\t */\r\n\tpublic JdbcDBUpgrader(String checkSql, String usql) {\r\n\t\tupdSql = new String[1];\r\n\t\tthis.updSql[0] = usql;\r\n\t\tthis.checkSql = checkSql;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Instantiates a new jdbc db upgrader.\r\n\t * \r\n\t * @param checkSql the sql that checks if an upgrade is needed\r\n\t * @param usql an array of SQL statements to execute to perform the upgrade\r\n\t */\r\n\tpublic JdbcDBUpgrader(String checkSql, String[] usql) {\r\n\t\tthis.updSql = usql;\r\n\t\tthis.checkSql = checkSql;\r\n\t}\r\n\r\n\t/**\r\n\t * check if db Needs upgrade.\r\n\t * \r\n\t * @return true, if upgrade needed\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tprivate boolean needsUpgrade() throws Exception {\r\n\t\t\r\n\t\tif( checkSql == null )\r\n\t\t\treturn true;\r\n\t\t\r\n\t\ttry {\r\n\t\t\tJdbcDB.execSQL(checkSql);\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (e instanceof SQLException)\r\n\t\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Execute the upgrade SQL.\r\n\t *\r\n\t * @throws Exception the exception\r\n\t */\r\n\tprivate void performUpgrade() throws Exception {\r\n\t\tfor( int i = 0; i < updSql.length; i++ )\r\n\t\t{\r\n\t\t\tlog.info(\"Running Upgrade SQL:\" + updSql[i]);\r\n\t\t\tJdbcDB.execSQL(updSql[i]);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * run the upgrade check and then upgrade if needed\r\n\t */\r\n\tpublic void upgrade() {\r\n\t\ttry {\r\n\t\t\tif (needsUpgrade()) {\r\n\t\t\t\tperformUpgrade();\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/LinkJdbcDB.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.model.db.LinkDB;\r\nimport net.sf.borg.model.entity.Link;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\n/**\r\n * provides the JDBC layer for reading/writing Links\r\n */\r\nclass LinkJdbcDB extends JdbcBeanDB<Link> implements LinkDB {\r\n\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.EntityDB#addObj(net.sf.borg.model.entity.KeyedEntity)\r\n\t */\r\n\t@Override\r\n\tpublic void addObj(Link att) throws \r\n\t\t\tException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO links ( id, linktype, ownerkey, ownertype, path) \"\r\n\t\t\t\t\t\t+ \" VALUES \" + \"( ?, ?, ?, ?, ?)\");\r\n\r\n\t\t\r\n\t\tstmt.setInt(1, att.getKey());\r\n\t\tstmt.setString(2, att.getLinkType());\r\n\t\tstmt.setInt(3, JdbcDB.toInt(att.getOwnerKey()));\r\n\t\tstmt.setString(4, att.getOwnerType());\r\n\t\tstmt.setString(5, att.getPath());\r\n\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t\twriteCache(att);\r\n\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.EntityDB#delete(int)\r\n\t */\r\n\t@Override\r\n\tpublic void delete(int key) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"DELETE FROM links WHERE id = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t\tdelCache(key);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the keys.\r\n\t * \r\n\t * @return the keys\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<Integer> getKeys() throws Exception {\r\n\t\tArrayList<Integer> keys = new ArrayList<Integer>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT id FROM links\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(Integer.valueOf(rs.getInt(\"id\")));\r\n\t\t}\r\n\t\trs.close();\r\n        stmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.EntityDB#nextkey()\r\n\t */\r\n\t@Override\r\n\tpublic int nextkey() throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT MAX(id) FROM links\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n        stmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.EntityDB#newObj()\r\n\t */\r\n\t@Override\r\n\tpublic Link newObj() {\r\n\t\treturn (new Link());\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSOne(int)\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSOne(int key) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * FROM links WHERE id = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\t\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSAll()\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSAll() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * FROM links\");\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#createFrom(java.sql.ResultSet)\r\n\t */\r\n\t@Override\r\n\tLink createFrom(ResultSet r) throws SQLException {\r\n\t\tLink att = new Link();\r\n\t\tatt.setKey(r.getInt(\"id\"));\r\n\t\tatt.setLinkType(r.getString(\"linktype\"));\r\n\t\tatt.setOwnerKey(Integer.valueOf(r.getInt(\"ownerkey\")));\r\n\t\tatt.setOwnerType(r.getString(\"ownertype\"));\r\n\t\tatt.setPath(r.getString(\"path\"));\r\n\t\treturn att;\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.EntityDB#updateObj(net.sf.borg.model.entity.KeyedEntity)\r\n\t */\r\n\t@Override\r\n\tpublic void updateObj(Link att) throws \r\n\t\t\tException {\r\n\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"UPDATE links SET linktype = ?, ownerkey = ?, ownertype = ?, path = ?\"\r\n\t\t\t\t\t\t+ \" WHERE id = ?\");\r\n\r\n\t\r\n\t\tstmt.setString(1, att.getLinkType());\r\n\t\tstmt.setInt(2, att.getOwnerKey().intValue());\r\n\t\tstmt.setString(3, att.getOwnerType());\r\n\t\tstmt.setString(4, att.getPath());\r\n\t\tstmt.setInt(5, att.getKey());\r\n\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t\tdelCache(att.getKey());\r\n\t\twriteCache(att);\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.LinkDB#getLinks(int, java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Link> getLinks(int ownerkey, String ownertype)\r\n\t\t\tthrows SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * from links where ownerkey = ? and ownertype = ?\");\r\n\t\tResultSet r = null;\r\n\t\tList<Link> lst = new ArrayList<Link>();\r\n\t\ttry {\r\n\r\n\t\t\tstmt.setInt(1, ownerkey);\r\n\t\t\tstmt.setString(2, ownertype);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tLink s = createFrom(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/MemoJdbcDB.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport net.sf.borg.model.db.MemoDB;\r\nimport net.sf.borg.model.entity.Memo;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\n/**\r\n * provides the JDBC layer for reading/writing Memos.\r\n */\r\nclass MemoJdbcDB implements MemoDB {\r\n\r\n\r\n\tpublic MemoJdbcDB()\r\n\t{\r\n\t\t\r\n\t}\r\n\t\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#addMemo(net.sf.borg.model.entity.Memo)\r\n\t */\r\n\t@Override\r\n\tpublic void addMemo(Memo m) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO memos ( memoname, memotext, encrypted ) \"\r\n\t\t\t\t\t\t+ \" VALUES \" + \"( ?, ?, ? )\");\r\n\r\n\t\tstmt.setString(1, m.getMemoName());\r\n\t\tstmt.setString(2, m.getMemoText());\r\n\t\tif( m.isEncrypted())\r\n\t\t\tstmt.setString(3, \"Y\");\r\n\t\telse\r\n\t\t\tstmt.setString(3, \"N\");\r\n\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#delete(java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic void delete(String name) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"DELETE FROM memos WHERE memoname = ?\");\r\n\t\tstmt.setString(1, name);\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#getNames()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<String> getNames() throws Exception {\r\n\t\tArrayList<String> keys = new ArrayList<String>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT memoname FROM memos ORDER BY memoname\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(rs.getString(\"memoname\"));\r\n\t\t}\r\n\t\trs.close();\r\n        stmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\t\r\n\tprivate static PreparedStatement getPSOne(String name) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM memos WHERE memoname = ?\");\r\n\t\tstmt.setString(1, name);\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\r\n\r\n\tprivate static PreparedStatement getPSAll() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM memos\");\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t\r\n\t/**\r\n\t * create a Memo from a result set\r\n\t * @param r the result set\r\n\t * @return the Memo object\r\n\t * @throws SQLException\r\n\t */\r\n\tprivate static Memo createFrom(ResultSet r) throws SQLException {\r\n\t\tMemo m = new Memo();\r\n\r\n\t\tm.setMemoName(r.getString(\"memoname\"));\r\n\t\tm.setMemoText(r.getString(\"memotext\"));\r\n\t\tString enc = r.getString(\"encrypted\");\r\n\t\tif( enc != null && enc.equals(\"Y\"))\r\n\t\t\tm.setEncrypted(true);\r\n\r\n\t\treturn m;\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#readAll()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Memo> readAll() throws Exception {\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tstmt = getPSAll();\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tList<Memo> lst = new ArrayList<Memo>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tMemo bean = createFrom(r);\r\n\t\t\t\tlst.add(bean);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#readMemo(java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic Memo readMemo(String name) throws Exception {\r\n\r\n\t\tPreparedStatement stmt = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tMemo m = null;\r\n\t\t\tstmt = getPSOne(name);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tm = createFrom(r);\r\n\t\t\t}\r\n\t\t\treturn m;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.db.MemoDB#updateMemo(net.sf.borg.model.entity.Memo)\r\n\t */\r\n\t@Override\r\n\tpublic void updateMemo(Memo m) throws Exception {\r\n\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"UPDATE memos SET \"\r\n\t\t\t\t+ \" memotext = ?,\"\r\n\t\t\t\t+ \" encrypted = ?\"\r\n\t\t\t\t+ \" WHERE memoname = ?\");\r\n\r\n\t\tstmt.setString(1, m.getMemoText());\r\n\t\tif( m.isEncrypted())\r\n\t\t\tstmt.setString(2, \"Y\");\r\n\t\telse\r\n\t\t\tstmt.setString(2,\"N\");\r\n\t\tstmt.setString(3, m.getMemoName());\r\n\r\n\t\tstmt.executeUpdate();\r\n        stmt.close();\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/OptionJdbcDB.java",
    "content": "package net.sf.borg.model.db.jdbc;\n\nimport net.sf.borg.model.db.OptionDB;\nimport net.sf.borg.model.entity.Option;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nclass OptionJdbcDB implements OptionDB {\n\n\t/**\n\t * Gets an option value from the options table\n\t * \n\t * @param oname\n\t *            the option name\n\t * \n\t * @return the option value\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\t@Override\n\tpublic String getOption(String oname) throws Exception {\n\t\tString ret = null;\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\n\t\t\t\t\"SELECT value FROM options WHERE name = ?\");\n\t\tstmt.setString(1, oname);\n\t\tResultSet rs = stmt.executeQuery();\n\t\tif (rs.next()) {\n\t\t\tret = rs.getString(\"value\");\n\t\t}\n\t\trs.close();\n\t\tstmt.close();\n\n\t\treturn (ret);\n\t}\n\n\t/**\n\t * Gets all options from the options table.\n\t * \n\t * @return a collection of options\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\t@Override\n\tpublic Collection<Option> getOptions() throws Exception {\n\t\tArrayList<Option> keys = new ArrayList<Option>();\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\n\t\t\t\t\"SELECT name, value FROM options\");\n\t\tResultSet rs = stmt.executeQuery();\n\t\twhile (rs.next()) {\n\t\t\tkeys.add(new Option(rs.getString(\"name\"), rs.getString(\"value\")));\n\t\t}\n\n\t\trs.close();\n\t\tstmt.close();\n\n\t\treturn (keys);\n\n\t}\n\n\t/**\n\t * Sets an option in the options table.\n\t * \n\t * @param option\n\t *            the option to set\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\t@Override\n\tpublic void setOption(Option option) throws Exception {\n\t\tString oname = option.getKey();\n\t\tString value = option.getValue();\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\n\t\t\t\t\t\"DELETE FROM options WHERE name = ?\");\n\t\t\tstmt.setString(1, oname);\n\t\t\tstmt.executeUpdate();\n\t\t\tstmt.close();\n\n\t\t} catch (Exception e) {\n\t\t\t// empty\n\t\t}\n\n\t\tif (value == null || value.equals(\"\"))\n\t\t\treturn;\n\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\n\t\t\t\t\"INSERT INTO options ( name, value ) \" + \"VALUES ( ?, ?)\");\n\n\t\tstmt.setString(1, oname);\n\t\tstmt.setString(2, value);\n\n\t\tstmt.executeUpdate();\n\t\tstmt.close();\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/TaskJdbcDB.java",
    "content": "/*\r\n This file is part of BORG.\r\n \r\n BORG is free software; you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation; either version 2 of the License, or\r\n (at your option) any later version.\r\n \r\n BORG is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with BORG; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\n Copyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.db.jdbc;\r\n\r\nimport java.sql.PreparedStatement;\r\nimport java.sql.ResultSet;\r\nimport java.sql.SQLException;\r\nimport java.util.ArrayList;\r\nimport java.util.Calendar;\r\nimport java.util.Collection;\r\nimport java.util.Date;\r\nimport java.util.List;\r\n\r\nimport net.sf.borg.model.db.TaskDB;\r\nimport net.sf.borg.model.entity.Project;\r\nimport net.sf.borg.model.entity.Subtask;\r\nimport net.sf.borg.model.entity.Task;\r\nimport net.sf.borg.model.entity.Tasklog;\r\n\r\n/**\r\n * this is the JDBC layer for access to the task table.\r\n */\r\nclass TaskJdbcDB extends JdbcBeanDB<Task> implements TaskDB {\r\n\r\n\tpublic TaskJdbcDB() {\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.EntityDB#addObj(net.sf.borg.model.entity.KeyedEntity)\r\n\t */\r\n\t@Override\r\n\tpublic void addObj(Task task) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO tasks ( tasknum, start_date, due_date, person_assigned,\"\r\n\t\t\t\t\t\t+ \" priority, state, type, description, resolution, category, close_date, project, summary, create_time, lastmod, uid, url) VALUES \"\r\n\t\t\t\t\t\t+ \"( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setInt(1, task.getKey());\r\n\r\n\t\tjava.util.Date sd = task.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tjava.util.Date dd = task.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(3, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(3, null);\r\n\r\n\t\tstmt.setString(4, task.getPersonAssigned());\r\n\t\tif (task.getPriority() != null)\r\n\t\t\tstmt.setInt(5, task.getPriority().intValue());\r\n\t\telse\r\n\t\t\tstmt.setInt(5, 1);\r\n\t\tstmt.setString(6, task.getState());\r\n\t\tstmt.setString(7, task.getType());\r\n\t\tstmt.setString(8, task.getDescription());\r\n\t\tstmt.setString(9, task.getResolution());\r\n\t\tstmt.setString(10, task.getCategory());\r\n\t\tjava.util.Date cd = task.getCompletionDate();\r\n\t\tif (cd != null)\r\n\t\t\tstmt.setDate(11, new java.sql.Date(cd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(11, null);\r\n\t\tif (task.getProject() != null)\r\n\t\t\tstmt.setInt(12, task.getProject().intValue());\r\n\t\telse\r\n\t\t\tstmt.setNull(12, java.sql.Types.INTEGER);\r\n\t\tstmt.setString(13, task.getSummary());\r\n\t\tstmt.setTimestamp(14, new java.sql.Timestamp(task.getCreateTime().getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(15, new java.sql.Timestamp(task.getLastMod().getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(16, task.getUid());\r\n\t\tstmt.setString(17, task.getUrl());\r\n\t\tstmt.executeUpdate();\r\n\r\n\t\twriteCache(task);\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#delete(int)\r\n\t */\r\n\t@Override\r\n\tpublic void delete(int key) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"DELETE FROM tasks WHERE tasknum = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t\tdelCache(key);\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the keys.\r\n\t * \r\n\t * @return the keys\r\n\t * \r\n\t * @throws Exception the exception\r\n\t */\r\n\tpublic Collection<Integer> getKeys() throws Exception {\r\n\t\tArrayList<Integer> keys = new ArrayList<Integer>();\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT tasknum FROM tasks\");\r\n\t\tResultSet rs = stmt.executeQuery();\r\n\t\twhile (rs.next()) {\r\n\t\t\tkeys.add(Integer.valueOf(rs.getInt(\"tasknum\")));\r\n\t\t}\r\n\t\trs.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn (keys);\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#nextkey()\r\n\t */\r\n\t@Override\r\n\tpublic int nextkey() throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT MAX(tasknum) FROM tasks\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.EntityDB#newObj()\r\n\t */\r\n\t@Override\r\n\tpublic Task newObj() {\r\n\t\treturn (new Task());\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSOne(int)\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSOne(int key) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM tasks WHERE tasknum = ?\");\r\n\t\tstmt.setInt(1, key);\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#getPSAll()\r\n\t */\r\n\t@Override\r\n\tPreparedStatement getPSAll() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM tasks\");\r\n\t\treturn stmt;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.jdbc.JdbcBeanDB#createFrom(java.sql.ResultSet)\r\n\t */\r\n\t@Override\r\n\tTask createFrom(ResultSet r) throws SQLException {\r\n\t\tTask task = new Task();\r\n\t\ttask.setKey(r.getInt(\"tasknum\"));\r\n\t\ttask.setStartDate(r.getDate(\"start_date\"));\r\n\t\tif (r.getDate(\"due_date\") != null)\r\n\t\t\ttask.setDueDate(new java.util.Date(r.getDate(\"due_date\").getTime()));\r\n\t\ttask.setPersonAssigned(r.getString(\"person_assigned\"));\r\n\t\ttask.setPriority(Integer.valueOf(r.getInt(\"priority\")));\r\n\t\ttask.setState(r.getString(\"state\"));\r\n\t\ttask.setType(r.getString(\"type\"));\r\n\t\ttask.setDescription(r.getString(\"description\"));\r\n\t\ttask.setResolution(r.getString(\"resolution\"));\r\n\t\ttask.setCategory(r.getString(\"category\"));\r\n\t\tif (r.getDate(\"close_date\") != null)\r\n\t\t\ttask.setCompletionDate(new java.util.Date(r.getDate(\"close_date\").getTime()));\r\n\t\ttask.setProject((Integer) r.getObject(\"project\"));\r\n\t\ttask.setSummary(r.getString(\"summary\"));\r\n\t\tif (r.getTimestamp(\"create_time\") != null)\r\n\t\t\ttask.setCreateTime(new java.util.Date(r.getTimestamp(\"create_time\").getTime()));\r\n\t\tif (r.getTimestamp(\"lastmod\") != null)\r\n\t\t\ttask.setLastMod(new java.util.Date(r.getTimestamp(\"lastmod\").getTime()));\r\n\t\ttask.setUid(r.getString(\"uid\"));\r\n\t\ttask.setUrl(r.getString(\"url\"));\r\n\r\n\t\treturn task;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.EntityDB#updateObj(net.sf.borg.model.entity.KeyedEntity)\r\n\t */\r\n\t@Override\r\n\tpublic void updateObj(Task task) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"UPDATE tasks SET  start_date = ?, due_date = ?, person_assigned = ?,\"\r\n\t\t\t\t\t\t+ \" priority = ?, state = ?, type = ?, description = ?, resolution = ?,\"\r\n\t\t\t\t\t\t+ \" category = ?, close_date = ?, project = ?, summary = ?, create_time = ?, lastmod = ?, uid = ?, url = ? WHERE tasknum = ?\");\r\n\r\n\t\tjava.util.Date sd = task.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(1, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(1, null);\r\n\r\n\t\tjava.util.Date dd = task.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tstmt.setString(3, task.getPersonAssigned());\r\n\t\tstmt.setInt(4, task.getPriority().intValue());\r\n\t\tstmt.setString(5, task.getState());\r\n\t\tstmt.setString(6, task.getType());\r\n\t\tstmt.setString(7, task.getDescription());\r\n\t\tstmt.setString(8, task.getResolution());\r\n\t\tstmt.setString(9, task.getCategory());\r\n\t\tjava.util.Date cd = task.getCompletionDate();\r\n\t\tif (cd != null)\r\n\t\t\tstmt.setDate(10, new java.sql.Date(cd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(10, null);\r\n\t\tif (task.getProject() != null)\r\n\t\t\tstmt.setInt(11, task.getProject().intValue());\r\n\t\telse\r\n\t\t\tstmt.setNull(11, java.sql.Types.INTEGER);\r\n\t\tstmt.setString(12, task.getSummary());\r\n\r\n\t\tstmt.setTimestamp(13, new java.sql.Timestamp(task.getCreateTime().getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(14, new java.sql.Timestamp(task.getLastMod().getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(15, task.getUid());\r\n\t\tstmt.setString(16, task.getUrl());\r\n\r\n\t\tstmt.setInt(17, task.getKey());\r\n\r\n\t\tstmt.executeUpdate();\r\n\r\n\t\tdelCache(task.getKey());\r\n\t\twriteCache(task);\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Create a subtask object from a result set.\r\n\t * \r\n\t * @param r the result set\r\n\t * \r\n\t * @return the subtask\r\n\t * \r\n\t * @throws SQLException the SQL exception\r\n\t */\r\n\tprivate static Subtask createSubtask(ResultSet r) throws SQLException {\r\n\t\tSubtask s = new Subtask();\r\n\t\ts.setKey(r.getInt(\"id\"));\r\n\t\ts.setTask(Integer.valueOf(r.getInt(\"task\")));\r\n\t\tif (r.getTimestamp(\"due_date\") != null)\r\n\t\t\ts.setDueDate(new java.util.Date(r.getTimestamp(\"due_date\").getTime()));\r\n\t\tif (r.getTimestamp(\"create_date\") != null)\r\n\t\t\ts.setStartDate(new java.util.Date(r.getTimestamp(\"create_date\").getTime()));\r\n\t\tif (r.getTimestamp(\"close_date\") != null)\r\n\t\t\ts.setCloseDate(new java.util.Date(r.getTimestamp(\"close_date\").getTime()));\r\n\t\ts.setDescription(r.getString(\"description\"));\r\n\t\tif (r.getTimestamp(\"create_time\") != null)\r\n\t\t\ts.setCreateTime(new java.util.Date(r.getTimestamp(\"create_time\").getTime()));\r\n\t\tif (r.getTimestamp(\"lastmod\") != null)\r\n\t\t\ts.setLastMod(new java.util.Date(r.getTimestamp(\"lastmod\").getTime()));\r\n\t\ts.setUid(r.getString(\"uid\"));\r\n\t\ts.setUrl(r.getString(\"url\"));\r\n\r\n\t\treturn s;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getSubTasks(int)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Subtask> getSubTasks(int taskid) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from subtasks where task = ?\");\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tstmt.setInt(1, taskid);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tList<Subtask> lst = new ArrayList<Subtask>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tSubtask s = createSubtask(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getSubTasks()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Subtask> getSubTasks() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from subtasks\");\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tList<Subtask> lst = new ArrayList<Subtask>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tSubtask s = createSubtask(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getSubTask(int)\r\n\t */\r\n\t@Override\r\n\tpublic Subtask getSubTask(int id) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from subtasks WHERE id = ?\");\r\n\t\tstmt.setInt(1, id);\r\n\t\tSubtask p = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tp = createSubtask(r);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn p;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#deleteSubTask(int)\r\n\t */\r\n\t@Override\r\n\tpublic void deleteSubTask(int id) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"DELETE FROM subtasks WHERE id = ?\");\r\n\t\tstmt.setInt(1, id);\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#addSubTask(net.sf.borg.model.entity.Subtask)\r\n\t */\r\n\t@Override\r\n\tpublic void addSubTask(Subtask s) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO subtasks ( id, create_date, due_date,\"\r\n\t\t\t\t\t\t+ \" close_date, description, task, create_time, lastmod, uid, url ) VALUES \"\r\n\t\t\t\t\t\t+ \"( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setInt(1, s.getKey());\r\n\r\n\t\tjava.util.Date sd = s.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tjava.util.Date dd = s.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(3, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(3, null);\r\n\r\n\t\tjava.util.Date cd = s.getCloseDate();\r\n\t\tif (cd != null)\r\n\t\t\tstmt.setDate(4, new java.sql.Date(cd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(4, null);\r\n\r\n\t\tstmt.setString(5, s.getDescription());\r\n\t\tstmt.setInt(6, s.getTask().intValue());\r\n\r\n\t\tstmt.setTimestamp(7, new java.sql.Timestamp(s.getCreateTime().getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(8, new java.sql.Timestamp(s.getLastMod().getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(9, s.getUid());\r\n\t\tstmt.setString(10, s.getUrl());\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.TaskDB#updateSubTask(net.sf.borg.model.entity.Subtask)\r\n\t */\r\n\t@Override\r\n\tpublic void updateSubTask(Subtask s) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"UPDATE subtasks SET create_date = ?, due_date = ?,\"\r\n\t\t\t\t\t\t+ \" close_date = ?, description = ?, task = ?, create_time = ?, lastmod = ?, uid = ?, url = ?  WHERE id = ?\");\r\n\r\n\t\tjava.util.Date sd = s.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(1, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(1, null);\r\n\r\n\t\tjava.util.Date dd = s.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tjava.util.Date cd = s.getCloseDate();\r\n\t\tif (cd != null)\r\n\t\t\tstmt.setDate(3, new java.sql.Date(cd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(3, null);\r\n\r\n\t\tstmt.setString(4, s.getDescription());\r\n\t\tstmt.setInt(5, s.getTask().intValue());\r\n\r\n\t\tstmt.setTimestamp(6, new java.sql.Timestamp(s.getCreateTime().getTime()), Calendar.getInstance());\r\n\t\tstmt.setTimestamp(7, new java.sql.Timestamp(s.getLastMod().getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(8, s.getUid());\r\n\t\tstmt.setString(9, s.getUrl());\r\n\r\n\t\tstmt.setInt(10, s.getKey());\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#nextSubTaskKey()\r\n\t */\r\n\t@Override\r\n\tpublic int nextSubTaskKey() throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT MAX(id) FROM subtasks\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/**\r\n\t * Get the next log key\r\n\t * \r\n\t * @return the next log key\r\n\t * \r\n\t * @throws SQLException the SQL exception\r\n\t */\r\n\tprivate static int nextLogKey() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT MAX(id) FROM tasklog\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#addLog(int, java.lang.String)\r\n\t */\r\n\t@Override\r\n\tpublic void addLog(int taskid, String desc) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO tasklog ( id, logtime, description, task ) VALUES \" + \"( ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setInt(1, nextLogKey());\r\n\t\tDate now = new Date();\r\n\t\tstmt.setTimestamp(2, new java.sql.Timestamp(now.getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(3, desc);\r\n\t\tstmt.setInt(4, taskid);\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#saveLog(net.sf.borg.model.entity.Tasklog)\r\n\t */\r\n\t@Override\r\n\tpublic void saveLog(Tasklog tlog) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO tasklog ( id, logtime, description, task ) VALUES \" + \"( ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setInt(1, nextLogKey());\r\n\t\tDate d = tlog.getLogTime();\r\n\t\tstmt.setTimestamp(2, new java.sql.Timestamp(d.getTime()), Calendar.getInstance());\r\n\t\tstmt.setString(3, tlog.getDescription());\r\n\t\tstmt.setInt(4, tlog.getTask().intValue());\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a tasklog object from a result set\r\n\t * \r\n\t * @param r the result set\r\n\t * \r\n\t * @return the tasklog object\r\n\t * \r\n\t * @throws SQLException the SQL exception\r\n\t */\r\n\tprivate static Tasklog createTasklog(ResultSet r) throws SQLException {\r\n\t\tTasklog s = new Tasklog();\r\n\t\ts.setKey(r.getInt(\"id\"));\r\n\t\ts.setTask(Integer.valueOf(r.getInt(\"task\")));\r\n\t\tif (r.getTimestamp(\"logtime\") != null)\r\n\t\t\ts.setLogTime(new java.util.Date(r.getTimestamp(\"logtime\").getTime()));\r\n\t\ts.setDescription(r.getString(\"description\"));\r\n\r\n\t\treturn s;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getLogs(int)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Tasklog> getLogs(int taskid) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from tasklog where task = ?\");\r\n\t\tResultSet r = null;\r\n\t\tList<Tasklog> lst = new ArrayList<Tasklog>();\r\n\t\ttry {\r\n\r\n\t\t\tstmt.setInt(1, taskid);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tTasklog s = createTasklog(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getLogs()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Tasklog> getLogs() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from tasklog\");\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tList<Tasklog> lst = new ArrayList<Tasklog>();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tTasklog s = createTasklog(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\t\t\treturn lst;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#addProject(net.sf.borg.model.entity.Project)\r\n\t */\r\n\t@Override\r\n\tpublic void addProject(Project p) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"INSERT INTO projects ( id,start_date, due_date,\"\r\n\t\t\t\t\t\t+ \" description, category, status, parent ) VALUES \" + \"( ?, ?, ?, ?, ?, ?, ?)\");\r\n\r\n\t\tstmt.setInt(1, p.getKey());\r\n\r\n\t\tjava.util.Date sd = p.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tjava.util.Date dd = p.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(3, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(3, null);\r\n\r\n\t\tstmt.setString(4, p.getDescription());\r\n\t\tstmt.setString(5, p.getCategory());\r\n\t\tstmt.setString(6, p.getStatus());\r\n\t\tif (p.getParent() != null)\r\n\t\t\tstmt.setInt(7, p.getParent().intValue());\r\n\t\telse\r\n\t\t\tstmt.setNull(7, java.sql.Types.INTEGER);\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#deleteProject(int)\r\n\t */\r\n\t@Override\r\n\tpublic void deleteProject(int id) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"DELETE FROM projects WHERE id = ?\");\r\n\t\tstmt.setInt(1, id);\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getProject(int)\r\n\t */\r\n\t@Override\r\n\tpublic Project getProject(int projectid) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM projects WHERE id = ?\");\r\n\t\tstmt.setInt(1, projectid);\r\n\t\tProject p = null;\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tp = createProject(r);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\r\n\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn p;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getProjects()\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Project> getProjects() throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from projects\");\r\n\t\tResultSet r = null;\r\n\t\tList<Project> lst = new ArrayList<Project>();\r\n\t\ttry {\r\n\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tProject s = createProject(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getTasks(int)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Task> getTasks(int projectid) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from tasks where project = ?\");\r\n\t\tResultSet r = null;\r\n\t\tList<Task> lst = new ArrayList<Task>();\r\n\t\ttry {\r\n\r\n\t\t\tstmt.setInt(1, projectid);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tTask s = createFrom(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#getSubProjects(int)\r\n\t */\r\n\t@Override\r\n\tpublic Collection<Project> getSubProjects(int projectid) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"SELECT * from projects where parent = ? ORDER BY start_date\");\r\n\t\tResultSet r = null;\r\n\t\tList<Project> lst = new ArrayList<Project>();\r\n\t\ttry {\r\n\r\n\t\t\tstmt.setInt(1, projectid);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tProject s = createProject(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see net.sf.borg.model.db.TaskDB#nextProjectKey()\r\n\t */\r\n\t@Override\r\n\tpublic int nextProjectKey() throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT MAX(id) FROM projects\");\r\n\t\tResultSet r = stmt.executeQuery();\r\n\t\tint maxKey = 0;\r\n\t\tif (r.next())\r\n\t\t\tmaxKey = r.getInt(1);\r\n\t\tr.close();\r\n\t\tstmt.close();\r\n\r\n\t\treturn ++maxKey;\r\n\t}\r\n\r\n\t/*\r\n\t * (non-Javadoc)\r\n\t * \r\n\t * @see\r\n\t * net.sf.borg.model.db.TaskDB#updateProject(net.sf.borg.model.entity.Project)\r\n\t */\r\n\t@Override\r\n\tpublic void updateProject(Project s) throws SQLException {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection()\r\n\t\t\t\t.prepareStatement(\"UPDATE projects SET start_date = ?, due_date = ?,\"\r\n\t\t\t\t\t\t+ \" description = ?, category = ?, status = ?, parent = ?  WHERE id = ?\");\r\n\r\n\t\tstmt.setInt(7, s.getKey());\r\n\r\n\t\tjava.util.Date sd = s.getStartDate();\r\n\t\tif (sd != null)\r\n\t\t\tstmt.setDate(1, new java.sql.Date(sd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(1, null);\r\n\r\n\t\tjava.util.Date dd = s.getDueDate();\r\n\t\tif (dd != null)\r\n\t\t\tstmt.setDate(2, new java.sql.Date(dd.getTime()));\r\n\t\telse\r\n\t\t\tstmt.setDate(2, null);\r\n\r\n\t\tstmt.setString(3, s.getDescription());\r\n\t\tstmt.setString(4, s.getCategory());\r\n\t\tstmt.setString(5, s.getStatus());\r\n\t\tif (s.getParent() != null)\r\n\t\t\tstmt.setInt(6, s.getParent().intValue());\r\n\t\telse\r\n\t\t\tstmt.setNull(6, java.sql.Types.INTEGER);\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a project from a result set\r\n\t * \r\n\t * @param r the result set\r\n\t * \r\n\t * @return the project\r\n\t * \r\n\t * @throws SQLException the SQL exception\r\n\t */\r\n\tprivate static Project createProject(ResultSet r) throws SQLException {\r\n\t\tProject s = new Project();\r\n\t\ts.setKey(r.getInt(\"id\"));\r\n\t\tif (r.getTimestamp(\"due_date\") != null)\r\n\t\t\ts.setDueDate(new java.util.Date(r.getTimestamp(\"due_date\").getTime()));\r\n\t\tif (r.getTimestamp(\"start_date\") != null)\r\n\t\t\ts.setStartDate(new java.util.Date(r.getTimestamp(\"start_date\").getTime()));\r\n\t\ts.setDescription(r.getString(\"description\"));\r\n\t\ts.setCategory(r.getString(\"category\"));\r\n\t\ts.setStatus(r.getString(\"status\"));\r\n\t\tint parent = r.getInt(\"parent\");\r\n\t\tif (r.wasNull())\r\n\t\t\ts.setParent(null);\r\n\t\telse\r\n\t\t\ts.setParent(Integer.valueOf(parent));\r\n\r\n\t\treturn s;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Collection<Task> getTasksByType(String type) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * from tasks where type = ?\");\r\n\t\tResultSet r = null;\r\n\t\tList<Task> lst = new ArrayList<Task>();\r\n\t\ttry {\r\n\r\n\t\t\tstmt.setString(1, type);\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\twhile (r.next()) {\r\n\t\t\t\tTask s = createFrom(r);\r\n\t\t\t\tlst.add(s);\r\n\t\t\t}\r\n\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tif (stmt != null)\r\n\t\t\t\tstmt.close();\r\n\t\t}\r\n\t\treturn lst;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void renameTaskType(String oldtype, String newtype) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"UPDATE tasks SET type = ? WHERE type = ?\");\r\n\r\n\t\tstmt.setString(1, newtype);\r\n\t\tstmt.setString(2, oldtype);\r\n\r\n\t\tstmt.executeUpdate();\r\n\t\tstmt.close();\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Subtask getSubTaskByUid(String uid) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM subtasks where uid = ?\");\r\n\t\tstmt.setString(1, uid);\r\n\r\n\t\tSubtask t = null;\r\n\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tt = createSubtask(r);\r\n\t\t\t}\r\n\t\t\treturn t;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tstmt.close();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Task getTaskByUid(String uid) throws Exception {\r\n\t\tPreparedStatement stmt = JdbcDB.getConnection().prepareStatement(\"SELECT * FROM tasks where uid = ?\");\r\n\t\tstmt.setString(1, uid);\r\n\r\n\t\tTask t = null;\r\n\r\n\t\tResultSet r = null;\r\n\t\ttry {\r\n\t\t\tr = stmt.executeQuery();\r\n\t\t\tif (r.next()) {\r\n\t\t\t\tt = createFrom(r);\r\n\t\t\t}\r\n\t\t\treturn t;\r\n\t\t} finally {\r\n\t\t\tif (r != null)\r\n\t\t\t\tr.close();\r\n\t\t\tstmt.close();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/jdbc/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Package jdbc contains all classes that deal directly with JDBC, including classes that read/write entities\n  and classes that manage JDBC URLs and Connections\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/db/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Package db contains interfaces that provide an implementation independent view of the low level \n  ORM layer. Currently, the only low level ORM layer is for JDBC. In past, a number of other layers\n  existed.\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Address.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n\n/**\n * The Address Entity\n */\n@XmlRootElement(name=\"Address\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper=false)\npublic class Address extends KeyedEntity<Address>  {\n\n\t\n\tprivate static final long serialVersionUID = 1996612351860988688L;\n\t\n\tprivate String FirstName;\n\tprivate String LastName;\n\tprivate String Nickname;\n\tprivate String Email;\n\tprivate String ScreenName;\n\tprivate String WorkPhone;\n\tprivate String HomePhone;\n\tprivate String CellPhone;\n\tprivate String Fax;\n\tprivate String Pager;\n\tprivate String StreetAddress;\n\tprivate String City;\n\tprivate String State;\n\tprivate String Zip;\n\tprivate String Country;\n\tprivate String Company;\n\tprivate String WorkStreetAddress;\n\tprivate String WorkCity;\n\tprivate String WorkState;\n\tprivate String WorkZip;\n\tprivate String WorkCountry;\n\tprivate String WebPage;\n\tprivate String Notes;\n\tprivate java.util.Date Birthday;\n\t@EqualsAndHashCode.Exclude private String vcard;\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Address clone() {\n\t\tAddress dst = new Address();\n\t\tdst.setKey( getKey());\n\t\tdst.setFirstName( getFirstName() );\n\t\tdst.setLastName( getLastName() );\n\t\tdst.setNickname( getNickname() );\n\t\tdst.setEmail( getEmail() );\n\t\tdst.setScreenName( getScreenName() );\n\t\tdst.setWorkPhone( getWorkPhone() );\n\t\tdst.setHomePhone( getHomePhone() );\n\t\tdst.setCellPhone( getCellPhone()  );\n\t\tdst.setFax( getFax() );\n\t\tdst.setPager( getPager() );\n\t\tdst.setStreetAddress( getStreetAddress() );\n\t\tdst.setCity( getCity() );\n\t\tdst.setState( getState() );\n\t\tdst.setZip( getZip() );\n\t\tdst.setCountry( getCountry() );\n\t\tdst.setCompany( getCompany() );\n\t\tdst.setWorkStreetAddress( getWorkStreetAddress() );\n\t\tdst.setWorkCity( getWorkCity() );\n\t\tdst.setWorkState( getWorkState() );\n\t\tdst.setWorkZip( getWorkZip() );\n\t\tdst.setWorkCountry( getWorkCountry() );\n\t\tdst.setWebPage( getWebPage() );\n\t\tdst.setNotes( getNotes() );\n\t\tdst.setBirthday( getBirthday() );\n\t\tdst.setVcard(getVcard());\n\t\treturn(dst);\n\t}\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Appointment.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport java.util.Date;\nimport java.util.Vector;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport net.sf.borg.common.EncryptionHelper;\nimport net.sf.borg.common.Resource;\n\n/**\n * The Appointment Entity\n */\n@XmlRootElement(name = \"Appointment\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper = false,exclude={\"createTime\", \"lastMod\"})\n// exclude key and encryption field from hashcode\npublic class Appointment extends KeyedEntity<Appointment> implements\n\t\tCalendarEntity, EncryptableEntity, SyncableEntity {\n\n\tprivate static final long serialVersionUID = 7225675837209156249L;\n\n\tprivate Date Date;\n\t/** The Duration in minutes */\n\tprivate Integer Duration = 0;\n\tprivate String Text;\n\t/**\n\t * The Skip list. - a list of repeat occurrences that are marked as skipped.\n\t */\n\tprivate Vector<String> SkipList;\n\t/** The Next todo - the date of the next todo for repeating todos */\n\tprivate Date NextTodo;\n\t/** The Vacation flag - 0 = not vacation 1 = vacation day, 2 = half day */\n\tprivate Integer Vacation = 0;\n\t/** The Holiday flag (1 = holiday) */\n\tprivate Integer Holiday = 0;\n\t/** The Private flag (1 = private) */\n\tprivate boolean Private;\n\t/** The number of Repeat Times. */\n\tprivate Integer Times = 1;\n\t/** The repeat Frequency. */\n\tprivate String Frequency;\n\t/** The Todo flag. */\n\tprivate boolean Todo;\n\tprivate String Color;\n\tprivate boolean RepeatFlag;\n\tprivate String Category;\n\t/** The Reminder times. See ReminderTimes.java */\n\tprivate String ReminderTimes;\n\t/**\n\t * The Untimed flag.Y = untimed provides a positive indication that an\n\t * appointment has no specific time of day\n\t */\n\tprivate String Untimed;\n\tprivate Date repeatUntil;\n\tprivate Integer priority = 5;\n\tprivate Date createTime;\n\tprivate Date lastMod;\n\tprivate String uid;\n\tprivate String url;\n\tprivate boolean encrypted = false;\n\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tprotected Appointment clone() {\n\t\tAppointment dst = new Appointment();\n\t\tdst.setKey(getKey());\n\t\tdst.setDate(getDate());\n\t\tdst.setDuration(getDuration());\n\t\tdst.setText(getText());\n\t\tVector<String> v = getSkipList();\n\t\tif (v != null) {\n\t\t\tdst.setSkipList((Vector<String>) v.clone());\n\t\t}\n\t\tdst.setNextTodo(getNextTodo());\n\t\tdst.setVacation(getVacation());\n\t\tdst.setHoliday(getHoliday());\n\t\tdst.setPrivate(isPrivate());\n\t\tdst.setTimes(getTimes());\n\t\tdst.setFrequency(getFrequency());\n\t\tdst.setTodo(isTodo());\n\t\tdst.setColor(getColor());\n\t\tdst.setRepeatFlag(isRepeatFlag());\n\t\tdst.setCategory(getCategory());\n\t\tdst.setReminderTimes(getReminderTimes());\n\t\tdst.setUntimed(getUntimed());\n\t\tdst.setEncrypted(isEncrypted());\n\t\tdst.setRepeatUntil(getRepeatUntil());\n\t\tdst.setPriority(getPriority());\n\t\tdst.setCreateTime(getCreateTime());\n\t\tdst.setLastMod(getLastMod());\n\t\tdst.setUid(getUid());\n\t\tdst.setUrl(getUrl());\n\t\treturn (dst);\n\t}\n\n\t@Override\n\tpublic void decrypt(String password) throws Exception {\n\t\tif (!isEncrypted())\n\t\t\treturn;\n\n\t\tEncryptionHelper helper = new EncryptionHelper(password);\n\t\tString clearText = helper.decrypt(this.getText());\n\t\tthis.setText(clearText);\n\t\tthis.setEncrypted(false);\n\t}\n\n\t@Override\n\tpublic void encrypt(String password) throws Exception {\n\t\tif (isEncrypted())\n\t\t\treturn;\n\n\t\tEncryptionHelper helper = new EncryptionHelper( password);\n\t\tString cipherText = helper.encrypt(this.getText());\n\t\tthis.setText(cipherText);\n\t\tthis.setEncrypted(true);\n\n\t}\n\n\t/**\n\t * get the appointment text or a generic message if encrypted\n\t * \n\t * @return\n\t */\n\tpublic String getClearText() {\n\t\tif (isEncrypted())\n\t\t\treturn (Resource.getResourceString(\"EncryptedItemShort\"));\n\t\treturn getText();\n\t}\n\n\t@Override\n\tpublic SyncableEntity.ObjectType getObjectType() {\n\t\treturn SyncableEntity.ObjectType.APPOINTMENT;\n\t}\n\tpublic String getTitle() {\n\t\t String t = getText();\n         if (t == null)\n             t = \"\";\n         int newlineIndex = t.indexOf('\\n');\n         if (newlineIndex != -1) {\n             return t.substring(0, newlineIndex);\n         } \n         \n         return getText();\n\t}\n\t\n\tpublic String getBody() {\n\t\t String t = getText();\n         if (t == null)\n             t = \"\";\n         int newlineIndex = t.indexOf('\\n');\n         if (newlineIndex != -1) {\n             return t.substring(newlineIndex + 1);\n         } \n\n         return \"\";\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/CalendarEntity.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.entity;\r\n\r\nimport java.util.Date;\r\n\r\n/**\r\n * Interface that needs to be implemented by any entity that can appear on the Calendar.\r\n * This interface would more properly belong in the UI package - but it would take some extra wrapper\r\n * classes that would be a waste.\r\n */\r\npublic interface CalendarEntity {\r\n\r\n\t/**\r\n\t * Gets the text.\r\n\t * \r\n\t * @return the text\r\n\t */\r\n    String getText();\r\n\t\r\n\t/**\r\n\t * Gets the color.\r\n\t * \r\n\t * @return the color\r\n\t */\r\n    String getColor();\r\n\t\r\n\t/**\r\n\t * Gets the date.\r\n\t * \r\n\t * @return the date\r\n\t */\r\n    Date getDate();\r\n\t\r\n\t/**\r\n\t * Gets the duration.\r\n\t * \r\n\t * @return the duration\r\n\t */\r\n    Integer getDuration();\r\n\t\r\n\t/**\r\n\t * Gets the todo flag.\r\n\t * \r\n\t * @return the todo flag\r\n\t */\r\n    boolean isTodo();\r\n\t\r\n\t/**\r\n\t * Gets the next todo date.\r\n\t * \r\n\t * @return the next todo date\r\n\t */\r\n    Date getNextTodo();\r\n\t\r\n\t/**\r\n\t * Gets the priority\r\n\t * @return the priority\r\n\t */\r\n    Integer getPriority();\r\n\t\r\n\tboolean isPrivate();\r\n\t\r\n\tDate getCreateTime();\r\n\tDate getLastMod();\r\n\t\r\n\tString getTitle();\r\n\tString getBody();\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/CheckList.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n\n/**\n * CheckList Entity. A CheckList holds a list of items that can be either checked or unchecked\n */\n@XmlRootElement(name=\"CheckList\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\npublic class CheckList implements Cloneable {\n\n\t/**\n\t * Item hold a single net.sf.borg.ui.checklist entry.\n\t */\n\t@XmlRootElement(name=\"Item\")\n\t@XmlAccessorType(XmlAccessType.FIELD)\n\t@Data\n\tpublic static class Item {\n\t\t\n\t\tprivate Boolean checked;\n\t\tprivate String text;\n\t\t\n\t\t@Override\n\t\tprotected Object clone() throws CloneNotSupportedException {\n\t\t\tItem copy = new Item();\n\t\t\tcopy.setText(text);\n\t\t\tcopy.setChecked(checked);\n\t\t\treturn copy;\n\t\t}\n\t}\n\t\n\tprivate String CheckListName;\n\tprivate List<Item> items = new ArrayList<Item>();\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tpublic CheckList clone() {\n\t\tCheckList dst = new CheckList();\n\t\tdst.setCheckListName( getCheckListName() );\n\t\tdst.items.addAll(items);\n\t\treturn(dst);\n\t}\n\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/EncryptableEntity.java",
    "content": "\npackage net.sf.borg.model.entity;\n\n/**\n * Interface for entities that can have encrypted fields. It is up to\n * the entity specific decrypt and encrypt methods to determine which fields are\n * to be encrypted\n *\n */\n\npublic interface EncryptableEntity {\n\t\n\t\n\t/**\n\t * decrypt the entity. This will use the password to get the borg encryption\n\t * key from the keystore and then will decrypt those fields in the entity that are encrypted.\n\t * the entity encrypted flag will be set to false.\n\t * @param password the keystore password\n\t * @throws Exception\n\t */\n    void decrypt(String password) throws Exception;\n\t\n\t/**\n\t * encrypt the entity. This will use the password to get the borg encryption\n\t * key from the keystore and then will encrypt those fields in the entity that are encryptable.\n\t * the entity encrypted flag will be set to true.\n\t * @param password the keystore password\n\t * @throws Exception\n\t */\n    void encrypt(String password) throws Exception;\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/KeyedEntity.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\n/*\r\n * KeyedEntity.java\r\n *\r\n * Created on January 1, 2004, 2:43 PM\r\n */\r\n\r\npackage net.sf.borg.model.entity;\r\n\r\nimport jakarta.xml.bind.annotation.XmlAccessType;\r\nimport jakarta.xml.bind.annotation.XmlAccessorType;\r\nimport jakarta.xml.bind.annotation.XmlElement;\r\nimport lombok.Data;\r\n\r\nimport java.io.Serializable;\r\n\r\n/**\r\n * Abstract base class for all Entities that are keyed by a simple integer key\r\n * \r\n */\r\n@XmlAccessorType(XmlAccessType.NONE)\r\n@Data\r\npublic abstract class KeyedEntity<T> implements Serializable\r\n{\r\n    \r\n    private static final long serialVersionUID = 1L;\r\n    \r\n\t@XmlElement(name=\"KEY\")\r\n    private int key = -1;\r\n        \r\n    \r\n    /**\r\n     * Copy the entity\r\n     * \r\n     * @return a copy\r\n     */\r\n    public T copy()\r\n    {\r\n    \treturn clone();\r\n    }\r\n    \r\n    /* (non-Javadoc)\r\n     * @see java.lang.Object#clone()\r\n     */\r\n    @Override\r\n    protected T clone(){\r\n    \tthrow new UnsupportedOperationException();\r\n    }\r\n    \r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/LabelEntity.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.entity;\r\n\r\nimport lombok.Data;\r\n\r\nimport java.util.Date;\r\n\r\n\r\n\r\n/**\r\n * This class is a non-persisted entity that is used when the model needs to package\r\n * a transient, calculated entity for the UI, such as a calculated holiday or birthday based on the\r\n * address book.\r\n */\r\n@Data\r\npublic class LabelEntity implements CalendarEntity {\r\n\r\n\tprivate String color;\r\n\tprivate Date date;\r\n\tprivate String Text;\r\n\tprivate String tooltipText;\r\n\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.entity.CalendarEntity#getDuration()\r\n\t */\r\n\t@Override\r\n\tpublic Integer getDuration() {\r\n\t\treturn Integer.valueOf(0);\r\n\t}\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.entity.CalendarEntity#getNextTodo()\r\n\t */\r\n\t@Override\r\n\tpublic Date getNextTodo() {\t\r\n\t\treturn null;\r\n\t}\r\n\t\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.entity.CalendarEntity#getTodo()\r\n\t */\r\n\t@Override\r\n\tpublic boolean isTodo() {\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Integer getPriority() {\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isPrivate() {\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic String getTooltipText() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn tooltipText;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Date getCreateTime() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Date getLastMod() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getTitle() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getBody() {\r\n\t\t// TODO Auto-generated method stub\r\n\t\treturn null;\r\n\t}\r\n\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Link.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlElement;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n\n/**\n * Link entity. A Link represents an association between an Entity (Appointment, Project, Task, Address) and \n * another Entity, a URL, or a File\n */\n@XmlRootElement(name=\"Link\")\n@XmlAccessorType(XmlAccessType.NONE)\n@Data\n@EqualsAndHashCode(callSuper=true)\npublic class Link extends KeyedEntity<Link> {\n\n\t\n\tprivate static final long serialVersionUID = 1476303921088473573L;\n\t\n\t/** The key of the owning KeyedEntity. */\n\t@XmlElement(name=\"OwnerKey\")\n\tprivate Integer ownerKey;\n\t\n\t/** The type of the KeyedEntity - mapped in LinkModel.java  */\n\t@XmlElement(name=\"OwnerType\")\n\tprivate String ownerType;\n\t\n\t/** The identifier of the link target - i.e. an entity id, file path, or url */\n\t@XmlElement(name=\"Path\")\n\tprivate String path;\n\t\n\t/** The link type - see LinkModel.LinkType. */\n\t@XmlElement(name=\"LinkType\")\n\tprivate String linkType;\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Link clone() {\n\t\tLink dst = new Link();\n\t\tdst.setKey( getKey());\n\t\tdst.setLinkType(getLinkType());\n\t\tdst.setOwnerKey( getOwnerKey() );\n\t\tdst.setOwnerType( getOwnerType() );\n\t\tdst.setPath( getPath() );\n\t\treturn(dst);\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Memo.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport net.sf.borg.common.EncryptionHelper;\n\n\n\n/**\n * Memo Entity. A Memo is a simple text entry keyed by a memo name. It remains simple\n * since it corresponds to the simple memo objects that can be synced to a palm pilot.\n */\n@XmlRootElement(name=\"Memo\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\npublic class Memo implements EncryptableEntity, Serializable {\n\n\t\n\tprivate static final long serialVersionUID = -6793670294661709573L;\n\t\n\tprivate String MemoName;\n\tprivate String MemoText;\n\tprivate Date Created;\n\tprivate Date Updated;\n\tprivate boolean encrypted = false;\n\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\tpublic Memo copy() {\n\t\tMemo dst = new Memo();\n\t\tdst.setMemoName( getMemoName() );\n\t\tdst.setMemoText( getMemoText() );\n\t\tdst.setEncrypted(isEncrypted());\n\t\tdst.setCreated(getCreated());\n\t\tdst.setUpdated(getUpdated());\n\t\treturn(dst);\n\t}\n\n\t@Override\n\tpublic void decrypt(String password) throws Exception {\n\t\tif( !isEncrypted() )\n\t\t\treturn;\n\t\t\n\t\t/* decrypt the memo text field */\n\t\tEncryptionHelper helper = new EncryptionHelper( password);\n\t\tString clearText = helper.decrypt(this.getMemoText());\n\t\tthis.setMemoText(clearText);\n\t\tthis.setEncrypted(false);\n\t}\n\n\t@Override\n\tpublic void encrypt(String password) throws Exception {\n\t\tif( isEncrypted() )\n\t\t\treturn;\n\t\t\n\t\t/* encrypt the memo text field */\n\t\tEncryptionHelper helper = new EncryptionHelper( password);\n\t\tString cipherText = helper.encrypt(this.getMemoText());\n\t\tthis.setMemoText(cipherText);\n\t\tthis.setEncrypted(true);\n\t\t\n\t}\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Option.java",
    "content": "/*\r\nThis file is part of BORG.\r\n \r\n    BORG is free software; you can redistribute it and/or modify\r\n    it under the terms of the GNU General Public License as published by\r\n    the Free Software Foundation; either version 2 of the License, or\r\n    (at your option) any later version.\r\n \r\n    BORG is distributed in the hope that it will be useful,\r\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n    GNU General Public License for more details.\r\n \r\n    You should have received a copy of the GNU General Public License\r\n    along with BORG; if not, write to the Free Software\r\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r\n \r\nCopyright 2003 by Mike Berger\r\n */\r\n\r\npackage net.sf.borg.model.entity;\r\n\r\nimport jakarta.xml.bind.annotation.XmlAccessType;\r\nimport jakarta.xml.bind.annotation.XmlAccessorType;\r\nimport jakarta.xml.bind.annotation.XmlElement;\r\nimport jakarta.xml.bind.annotation.XmlRootElement;\r\nimport lombok.Data;\r\n\r\n/**\r\n * Each BorgOption instance holds a single row from the options table in the database\r\n */\r\n@XmlRootElement(name=\"Option\")\r\n@XmlAccessorType(XmlAccessType.NONE)\r\n@Data\r\npublic class Option\r\n{\r\n\t\r\n\t/** for JAXB */\r\n\t@SuppressWarnings(\"unused\")\r\n\tprivate Option(){\r\n\t  // empty\r\n\t}\r\n\t\r\n\t/**\r\n\t * Instantiates a new borg option.\r\n\t * \r\n\t * @param key the key\r\n\t * @param value the value\r\n\t */\r\n\tpublic Option(String key, String value)\r\n\t{\r\n\t\tthis.key = key;\r\n\t\tthis.value = value;\r\n\t}\r\n\t\r\n\t\r\n\t/** The key. */\r\n\t@XmlElement(name=\"Key\")\r\n\tprivate String key;\r\n\t\r\n\t/** The value. */\r\n\t@XmlElement(name=\"Value\")\r\n\tprivate String value;\r\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Project.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.Theme;\n\nimport java.util.Date;\n\n\n\n/**\n * Project Entity - a project contains tasks and can have child projects\n */\n@XmlRootElement(name=\"Project\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper=true)\npublic class Project extends KeyedEntity<Project> implements CalendarEntity {\n\n\t\n\tprivate static final long serialVersionUID = -3250115693306817331L;\n\t\n\tprivate Date StartDate;\n\tprivate Date DueDate;\n\tprivate String Description;\n\tprivate String Category;\n\tprivate String Status;\n\tprivate Integer Parent;\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Project clone() {\n\t\tProject dst = new Project();\n\t\tdst.setKey( getKey());\n\t\tdst.setStartDate( getStartDate() );\n\t\tdst.setDueDate( getDueDate() );\n\t\tdst.setDescription( getDescription() );\n\t\tdst.setCategory( getCategory() );\n\t\tdst.setStatus( getStatus() );\n\t\tdst.setParent(getParent());\n\t\treturn(dst);\n\t}\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getColor()\n\t */\n\t@Override\n\tpublic String getColor()\n\t{\n\t\treturn Theme.TASKCOLOR;\n\t}\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getDuration()\n\t */\n\t@Override\n\tpublic Integer getDuration()\n\t{\n\t\treturn Integer.valueOf(0);\n\t}\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getDate()\n\t */\n\t@Override\n\tpublic Date getDate(){ return getDueDate(); }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getTodo()\n\t */\n\t@Override\n\tpublic boolean isTodo(){ return true; }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getNextTodo()\n\t */\n\t@Override\n\tpublic Date getNextTodo(){ return null; }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getText()\n\t */\n\t@Override\n\tpublic String getText(){\n\t\t// return the text as it should appear on the calendar\n\t\t String showabb = Prefs.getPref(PrefName.TASK_SHOW_ABBREV);\n\t\t String abb = \"\";\n         if (showabb.equals(\"true\"))\n             abb = \"PR\" + getKey() + \" \";\n         String de = abb + getDescription();\n         String tx = de.replace('\\n', ' ');\n         return tx;\n\t}\n\n\t@Override\n\tpublic Integer getPriority() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isPrivate() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Date getCreateTime() {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Date getLastMod() {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn getText();\n\t}\n\n\t@Override\n\tpublic String getBody() {\n\t\treturn getDescription();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Subtask.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.TaskModel;\n\nimport java.util.Date;\n\n/**\n * Subtask entity\n */\n@XmlRootElement(name = \"Subtask\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper = true, exclude = {\"lastMod\",\"createTime\"})\npublic class Subtask extends KeyedEntity<Subtask> implements CalendarEntity, SyncableEntity {\n\n\tprivate static final long serialVersionUID = -5794908342032518360L;\n\n\tprivate Date StartDate;\n\tprivate Date CloseDate;\n\tprivate Date DueDate;\n\tprivate String Description;\n\tprivate Integer Task;\n\tprivate Date createTime;\n\tprivate Date lastMod;\n\tprivate String uid;\n\tprivate String url;\n\n\t// cached task description\n\tprivate String taskDesc = null;\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Subtask clone() {\n\t\tSubtask dst = new Subtask();\n\t\tdst.setKey(getKey());\n\t\tdst.setStartDate(getStartDate());\n\t\tdst.setCloseDate(getCloseDate());\n\t\tdst.setDueDate(getDueDate());\n\t\tdst.setDescription(getDescription());\n\t\tdst.setTask(getTask());\n\t\tdst.setCreateTime(getCreateTime());\n\t\tdst.setLastMod(getLastMod());\n\t\tdst.setUid(getUid());\n\t\tdst.setUrl(getUrl());\n\t\treturn (dst);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getColor()\n\t */\n\t@Override\n\tpublic String getColor() {\n\t\treturn \"taskColor\";\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getDate()\n\t */\n\t@Override\n\tpublic Date getDate() {\n\t\treturn getDueDate();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getDuration()\n\t */\n\t@Override\n\tpublic Integer getDuration() {\n\t\treturn Integer.valueOf(0);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getNextTodo()\n\t */\n\t@Override\n\tpublic Date getNextTodo() {\n\t\treturn null;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getText()\n\t */\n\t@Override\n\tpublic String getText() {\n\t\t// return the text as it should appear on the calendar\n\t\tString showabb = Prefs.getPref(PrefName.TASK_SHOW_ABBREV);\n\t\tString abb = \"\";\n\t\tif (showabb.equals(\"true\"))\n\t\t\tabb = \"BT\" + getTask() + \"/ST\" + getKey() + \" \";\n\t\telse {\n\t\t\tif (taskDesc == null) {\n\t\t\t\tTask t;\n\t\t\t\ttry {\n\t\t\t\t\tt = TaskModel.getReference().getTask(Task);\n\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\ttaskDesc = t.getSummary();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tabb = \"[\" + taskDesc + \"] \";\n\t\t}\n\t\tString de = abb + getDescription();\n\t\tString tx = de.replace('\\n', ' ');\n\t\treturn tx;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.entity.CalendarEntity#getTodo()\n\t */\n\t@Override\n\tpublic boolean isTodo() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Integer getPriority() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isPrivate() {\n\t\treturn false;\n\t}\n\t\n\t@Override\n\tpublic ObjectType getObjectType() {\n\t\treturn ObjectType.SUBTASK;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn getText();\n\t}\n\n\t@Override\n\tpublic String getBody() {\n\t\treturn getDescription();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/SyncableEntity.java",
    "content": "package net.sf.borg.model.entity;\n\nimport java.util.Date;\n\npublic interface SyncableEntity {\n\t\n\tenum ObjectType {\n\t\tAPPOINTMENT, TASK, PROJECT, SUBTASK, REMOTE\n\t}\n\t\n\tint getKey();\n\t\n\tDate getCreateTime();\n\n\tDate getLastMod();\n\n\tString getUid();\n\n\tString getUrl();\n\t\n\tObjectType getObjectType();\n\n//\tpublic void setCreateTime(Date d);\n//\n//\tpublic void setLastMod(Date d);\n//\n//\tpublic void setUid(String s);\n//\n//\tpublic void setUrl(String s);\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Task.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.Theme;\n\nimport java.util.Date;\n\n\n\n/**\n * The Task Entity\n */\n@XmlRootElement(name=\"Task\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper=true)\npublic class Task extends KeyedEntity<Task> implements CalendarEntity, SyncableEntity {\n\n\t\n\tprivate static final long serialVersionUID = -8980203293028263282L;\n\t\n\tprivate Date StartDate;\n\tprivate Date CompletionDate;\n\tprivate Date DueDate;\n\tprivate String PersonAssigned;\n\tprivate Integer Priority;\n\tprivate String State;\n\tprivate String Type;\n\tprivate String Description;\n\tprivate String Resolution;\n\tprivate String Category;\n\tprivate Integer Project;\n\tprivate String summary;\n\tprivate Date createTime;\n\tprivate Date lastMod;\n\tprivate String uid;\n\tprivate String url;\n\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getColor()\n\t */\n\t@Override\n\tpublic String getColor()\n\t{\n\t\treturn Theme.TASKCOLOR;\n\t}\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getDuration()\n\t */\n\t@Override\n\tpublic Integer getDuration()\n\t{\n\t\treturn Integer.valueOf(0);\n\t}\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getDate()\n\t */\n\t@Override\n\tpublic Date getDate(){ return getDueDate(); }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getTodo()\n\t */\n\t@Override\n\tpublic boolean isTodo(){ return true; }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getNextTodo()\n\t */\n\t@Override\n\tpublic Date getNextTodo(){ return null; }\n\t\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.CalendarEntity#getText()\n\t */\n\t@Override\n\tpublic String getText(){\n\t\t// return the text as it should appear on the calendar\n\t\t String showabb = Prefs.getPref(PrefName.TASK_SHOW_ABBREV);\n\t\t String abb = \"\";\n         if (showabb.equals(\"true\"))\n             abb = \"BT\" + getKey() + \" \";\n         return abb + getSummary();\n\t}\n\t\n\t// for backwards compatibility\n\tpublic String getSummary(){\n\t\tif( summary != null && !summary.isEmpty())\n\t\t\treturn summary;\n\t\t\n\t\tif( Description != null && !Description.isEmpty())\n\t\t{\n\t\t\tString de = Description;\n\t\t\tif( de.indexOf('\\n') != -1)\n\t\t\t\tde = de.substring(0,de.indexOf('\\n'));\n\t\t\treturn de;\n\t\t}\n\t\t\n\t\treturn \"\";\n\t}\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Task clone() {\n\t\tTask dst = new Task();\n\t\tdst.setKey( getKey());\n\t\tdst.setStartDate( getStartDate() );\n\t\tdst.setCompletionDate( getCompletionDate() );\n\t\tdst.setDueDate( getDueDate() );\n\t\tdst.setPersonAssigned( getPersonAssigned() );\n\t\tdst.setPriority( getPriority() );\n\t\tdst.setState( getState() );\n\t\tdst.setType( getType() );\n\t\tdst.setDescription( getDescription() );\n\t\tdst.setResolution( getResolution() );\n\t\tdst.setCategory( getCategory() );\n\t\tdst.setProject( getProject() );\n\t\tdst.setSummary( getSummary() );\n\t\tdst.setCreateTime(getCreateTime());\n\t\tdst.setLastMod(getLastMod());\n\t\tdst.setUid(getUid());\n\t\tdst.setUrl(getUrl());\n\t\treturn(dst);\n\t}\n\n\t@Override\n\tpublic boolean isPrivate() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ObjectType getObjectType() {\n\t\treturn ObjectType.TASK;\n\t}\n\n\t@Override\n\tpublic String getTitle() {\n\t\treturn getSummary();\n\t}\n\n\t@Override\n\tpublic String getBody() {\n\t\treturn getDescription();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/Tasklog.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.entity;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n\n\n/**\n * The Tasklog Entity\n */\n@XmlRootElement(name=\"Tasklog\")\n@XmlAccessorType(XmlAccessType.FIELD)\n@Data\n@EqualsAndHashCode(callSuper=true)\npublic class Tasklog extends KeyedEntity<Tasklog> {\n\n\n\tprivate static final long serialVersionUID = -7296390517941361874L;\n\n\tprivate java.util.Date logTime;\n\tprivate String Description;\n\tprivate Integer Task;\n\t\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.entity.KeyedEntity#clone()\n\t */\n\t@Override\n\tprotected Tasklog clone() {\n\t\tTasklog dst = new Tasklog();\n\t\tdst.setKey( getKey());\n\t\tdst.setLogTime( getLogTime() );\n\t\tdst.setDescription( getDescription() );\n\t\tdst.setTask( getTask() );\n\t\treturn(dst);\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/entity/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Contains the data objects managed by the data model\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  The Model package and its sub-packages provide the data model layer for borg\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/RecurrenceRule.java",
    "content": "package net.sf.borg.model.sync;\n\nimport java.text.ParseException;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.TimeZone;\nimport java.util.logging.Logger;\n\nimport net.fortuna.ical4j.model.Recur;\nimport net.fortuna.ical4j.model.WeekDay;\nimport net.fortuna.ical4j.model.WeekDayList;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.entity.Appointment;\n\npublic class RecurrenceRule {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tstatic public void setRecur(Appointment ap, Recur recur) throws ParseException {\n\n\t\tString freq = recur.getFrequency();\n\t\tint interval = recur.getInterval();\n\t\tif (freq.equals(Recur.DAILY)) {\n\t\t\tif (interval > 1) {\n\t\t\t\tap.setFrequency(Repeat.NDAYS + \",\" + interval);\n\t\t\t} else\n\t\t\t\tap.setFrequency(Repeat.DAILY);\n\t\t} else if (freq.equals(Recur.WEEKLY)) {\n\t\t\tif (interval == 2) {\n\t\t\t\tap.setFrequency(Repeat.BIWEEKLY);\n\t\t\t} else if (interval > 2) {\n\t\t\t\tap.setFrequency(Repeat.NWEEKS + \",\" + interval);\n\t\t\t} else {\n\t\t\t\tap.setFrequency(Repeat.WEEKLY);\n\n\t\t\t\t// BORG can only handle daylist for weekly\n\t\t\t\tWeekDayList dl = recur.getDayList();\n\t\t\t\tif (dl != null && !dl.isEmpty()) {\n\t\t\t\t\tString f = Repeat.DAYLIST;\n\t\t\t\t\tf += \",\";\n\t\t\t\t\tfor (Object o : dl) {\n\t\t\t\t\t\tWeekDay wd = (WeekDay) o;\n\t\t\t\t\t\tf += WeekDay.getCalendarDay(wd);\n\t\t\t\t\t}\n\t\t\t\t\tap.setFrequency(f);\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else if (freq.equals(Recur.MONTHLY)) {\n\t\t\tif (interval > 1) {\n\t\t\t\tap.setFrequency(Repeat.NMONTHS + \",\" + interval);\n\t\t\t} else\n\t\t\t\tap.setFrequency(Repeat.MONTHLY);\n\t\t} else if (freq.equals(Recur.YEARLY)) {\n\t\t\tif (interval > 1) {\n\t\t\t\tap.setFrequency(Repeat.NYEARS + \",\" + interval);\n\t\t\t} else\n\t\t\t\tap.setFrequency(Repeat.YEARLY);\n\t\t} else {\n\t\t\tlog.warning(\"WARNING: Cannot handle frequency of [\" + freq + \"], for appt [\" + ap.toString()\n\t\t\t\t\t+ \"], adding first occurrence only\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tDate until = recur.getUntil();\n\t\tif (until != null) {\n\t\t\tlong u = until.getTime() - TimeZone.getDefault().getOffset(until.getTime());\n\t\t\tap.setRepeatUntil(new Date(u));\n\t\t} else {\n\t\t\tint times = recur.getCount();\n\t\t\tif (times < 1)\n\t\t\t\ttimes = 9999;\n\t\t\tap.setTimes(Integer.valueOf(times));\n\t\t}\n\n\t\tap.setRepeatFlag(true);\n\n\t\t\n\t}\n\t\n\n\tstatic public String getRRule(Appointment ap) {\n\t\tString rec = \"FREQ=\";\n\t\tString freq = Repeat.getFreq(ap.getFrequency());\n\n\t\tif (freq == null || freq.equals(Repeat.DAILY)) {\n\t\t\trec += \"DAILY\";\n\t\t} else if (freq.equals(Repeat.WEEKLY)) {\n\t\t\trec += \"WEEKLY\";\n\t\t} else if (freq.equals(Repeat.BIWEEKLY)) {\n\t\t\trec += \"WEEKLY;INTERVAL=2\";\n\t\t} else if (freq.equals(Repeat.MONTHLY)) {\n\t\t\tDate dd = ap.getDate();\n\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\tgc.setTime(dd);\n\t\t\trec += \"MONTHLY;BYMONTHDAY=\" + gc.get(java.util.Calendar.DATE);\n\t\t} else if (freq.equals(Repeat.MONTHLY_DAY)) {\n\t\t\tDate dd = ap.getDate();\n\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\tgc.setTime(dd);\n\t\t\tint dayOfWeek = gc.get(java.util.Calendar.DAY_OF_WEEK);\n\t\t\tint dayOfWeekMonth = gc.get(java.util.Calendar.DAY_OF_WEEK_IN_MONTH);\n\t\t\tString[] days = new String[] { \"SU\", \"MO\", \"TU\", \"WE\", \"TH\", \"FR\", \"SA\" };\n\t\t\trec += \"MONTHLY;BYDAY=\" + dayOfWeekMonth + days[dayOfWeek - 1];\n\t\t} else if (freq.equals(Repeat.MONTHLY_DAY_LAST)) {\n\t\t\tDate dd = ap.getDate();\n\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\tgc.setTime(dd);\n\t\t\tint dayOfWeek = gc.get(java.util.Calendar.DAY_OF_WEEK);\n\t\t\tString[] days = new String[] { \"SU\", \"MO\", \"TU\", \"WE\", \"TH\", \"FR\", \"SA\" };\n\t\t\trec += \"MONTHLY;BYDAY=\" + \"-1\" + days[dayOfWeek - 1];\n\t\t} else if (freq.equals(Repeat.YEARLY)) {\n\t\t\trec += \"YEARLY\";\n\t\t} else if (freq.equals(Repeat.NDAYS)) {\n\t\t\trec += \"DAILY;INTERVAL=\" + Repeat.getNValue(ap.getFrequency());\n\t\t} else if (freq.equals(Repeat.NWEEKS)) {\n\t\t\trec += \"WEEKLY;INTERVAL=\" + Repeat.getNValue(ap.getFrequency());\n\t\t} else if (freq.equals(Repeat.NMONTHS)) {\n\t\t\trec += \"MONTHLY;INTERVAL=\" + Repeat.getNValue(ap.getFrequency());\n\t\t} else if (freq.equals(Repeat.NYEARS)) {\n\t\t\trec += \"YEARLY;INTERVAL=\" + Repeat.getNValue(ap.getFrequency());\n\t\t} else if (freq.equals(Repeat.WEEKDAYS)) {\n\t\t\trec += \"WEEKLY;BYDAY=MO,TU,WE,TH,FR\";\n\t\t} else if (freq.equals(Repeat.MWF)) {\n\t\t\trec += \"WEEKLY;BYDAY=MO,WE,FR\";\n\t\t} else if (freq.equals(Repeat.WEEKENDS)) {\n\t\t\trec += \"WEEKLY;BYDAY=SU,SA\";\n\t\t} else if (freq.equals(Repeat.TTH)) {\n\t\t\trec += \"WEEKLY;BYDAY=TU,TH\";\n\t\t} else if (freq.equals(Repeat.DAYLIST)) {\n\t\t\tString[] days = new String[] { \"SU\", \"MO\", \"TU\", \"WE\", \"TH\", \"FR\", \"SA\" };\n\t\t\trec += \"WEEKLY;BYDAY=\";\n\t\t\tCollection<Integer> c = Repeat.getDaylist(ap.getFrequency());\n\t\t\tIterator<Integer> it = c.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tInteger i = it.next();\n\t\t\t\trec += days[i - 1];\n\t\t\t\tif (it.hasNext())\n\t\t\t\t\trec += \",\";\n\t\t\t}\n\n\t\t} else {\n\t\t\tlog.warning(\"Could not export appt \" + ap.getKey() + ap.getText());\n\t\t\treturn null;\n\t\t}\n\n\t\tif (ap.getTimes().intValue() != 9999) {\n\t\t\trec += \";COUNT=\" + Repeat.calculateTimes(ap);\n\t\t}\n\n\t\treturn rec;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/SubscribedCalendars.java",
    "content": "package net.sf.borg.model.sync;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Writer;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.CalendarEntityProvider;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.SearchCriteria;\nimport net.sf.borg.model.Searchable;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.LabelEntity;\n\npublic class SubscribedCalendars extends Model implements CalendarEntityProvider, Searchable<LabelEntity> {\n\n\t/** The singleton */\n\tstatic private final SubscribedCalendars self_ = new SubscribedCalendars();\n\n\t/**\n\t * Gets the singleton reference.\n\t * \n\t * @return the singleton reference\n\t */\n\tpublic static SubscribedCalendars getReference() {\n\t\treturn (self_);\n\t}\n\n\tprivate HashMap<String, HashMap<Integer, Collection<LabelEntity>>> calmap = new HashMap<String, HashMap<Integer, Collection<LabelEntity>>>();\n\tprivate boolean show = true; // show/hide subscribed events\n\tprivate boolean cacheLoaded = false;\n\t\n\tpublic void setShow(boolean b) {\n\t\tshow = b;\n\t\trefresh();\n\t}\n\t\n\tpublic boolean isShowing() {\n\t\treturn show;\n\t}\n\t\n\tpublic void addEvent(Appointment ap, String calendarId) {\n\n\t\t\n\n\t\tHashMap<Integer, Collection<LabelEntity>> daymap = calmap.get(calendarId);\n\t\tif (daymap == null) {\n\t\t\tdaymap = new HashMap<Integer, Collection<LabelEntity>>();\n\t\t\tcalmap.put(calendarId, daymap);\n\t\t}\n\n\t\tif (!Repeat.isRepeating(ap)) {\n\n\t\t\tint dkey = DateUtil.dayOfEpoch(ap.getDate());\n\n\t\t\tCollection<LabelEntity> day = daymap.get(dkey);\n\t\t\tif (day == null) {\n\t\t\t\tday = new ArrayList<LabelEntity>();\n\t\t\t\tdaymap.put(dkey, day);\n\t\t\t}\n\t\t\t\n\t\t\tLabelEntity e = new LabelEntity();\n\t\t\te.setText(formatText(ap, false));\n\t\t\te.setTooltipText(formatText(ap, true));\n\t\t\te.setColor(Theme.HOLIDAYCOLOR);\n\t\t\te.setDate(ap.getDate());\n\n\t\t\tday.add(e);\n\n\t\t} else {\n\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tint curyr = cal.get(Calendar.YEAR);\n\n\t\t\t// repeats\n\t\t\tcal.setTime(ap.getDate());\n\n\t\t\tRepeat repeat = new Repeat(cal, ap.getFrequency());\n\n\t\t\tint tm = Repeat.calculateTimes(ap);\n\n\t\t\tint apptYear = cal.get(Calendar.YEAR);\n\n\t\t\t// ok, plod through the repeats now\n\t\t\tfor (int i = 0; i < tm; i++) {\n\t\t\t\tCalendar current = repeat.current();\n\t\t\t\tif (current == null) {\n\t\t\t\t\trepeat.next();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// get the day key for the repeat\n\t\t\t\tint rkey = DateUtil.dayOfEpoch(current.getTime());\n\n\t\t\t\tint cyear = current.get(Calendar.YEAR);\n\n\t\t\t\t// limit the repeats to 10 years\n\t\t\t\tif (cyear > curyr + 10 && cyear > apptYear + 10)\n\t\t\t\t\tbreak;\n\n\t\t\t\tCollection<LabelEntity> day = daymap.get(rkey);\n\t\t\t\tif (day == null) {\n\t\t\t\t\tday = new ArrayList<LabelEntity>();\n\t\t\t\t\tdaymap.put(rkey, day);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tLabelEntity e = new LabelEntity();\n\t\t\t\te.setText(formatText(ap, false));\n\t\t\t\te.setTooltipText(formatText(ap, true));\n\t\t\t\te.setColor(Theme.HOLIDAYCOLOR);\n\t\t\t\te.setDate(current.getTime());\n\t\t\t\t\n\t\t\t\tday.add(e);\n\n\t\t\t\trepeat.next();\n\t\t\t}\n\t\t}\n\n\t}\n\n\t//public void removeCal(String calendarId) {\n\t//\tcalmap.remove(calendarId);\n\t//}\n\n\tpublic void removeCals() {\n\t\tcalmap.clear();\n\t\tdeleteCache();\n\t}\n\n\t@Override\n\tpublic List<CalendarEntity> getEntities(Date d) {\n\t\t\n\t\tif( !cacheLoaded )\n\t\t{\n\t\t\tloadCache();\n\t\t\tcacheLoaded = true;\n\t\t}\n\t\t\t\t\n\t\tArrayList<CalendarEntity> l = new ArrayList<CalendarEntity>();\n\t\t\n\t\tif( !show ) return l;\n\t\t\n\t\tfor (HashMap<Integer, Collection<LabelEntity>> cal : calmap.values()) {\n\t\t\tCollection<LabelEntity> c = cal.get(DateUtil.dayOfEpoch(d));\n\t\t\tif (c != null)\n\t\t\t\tl.addAll(c);\n\t\t}\n\n\t\treturn l;\n\t}\n\n\t@Override\n\tpublic void export(Writer fw) throws Exception {\n\t\t// do not export subscribed cals\n\n\t}\n\n\t@Override\n\tpublic void importXml(InputStream is) throws Exception {\n\t\t// do nothing\n\n\t}\n\n\t@Override\n\tpublic String getExportName() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getInfo() throws Exception {\n\t\treturn null;\n\t}\n\n\tpublic void refresh() {\n\t\tsuper.refreshListeners();\n\t}\n\n\tpublic String formatText(Appointment appt, boolean force_full_text) {\n\n\t\tif (appt.getText() == null) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tCalendar day = new GregorianCalendar();\n\t\tday.setTime(appt.getDate());\n\n\t\tString theFormattedText = \"\";\n\n\t\t// add time in front of the appt text\n\t\tif (!AppointmentModel.isNote(appt)) {\n\t\t\tDate d = appt.getDate();\n\t\t\tif (d != null) {\n\t\t\t\tSimpleDateFormat sdf = AppointmentModel.getTimeFormat();\n\t\t\t\ttheFormattedText += sdf.format(d) + \" \";\n\t\t\t}\n\t\t}\n\n\t\t// if the text is empty - skip it - should never be\n\t\tString xx = appt.getText();\n\t\tString trunc = Prefs.getPref(PrefName.TRUNCAPPT);\n\t\tif (!force_full_text && trunc.equals(\"true\")) {\n\t\t\t// !!!!! only show first line of appointment text !!!!!!\n\t\t\tint ii = xx.indexOf('\\n');\n\t\t\tif (ii != -1) {\n\t\t\t\ttheFormattedText += xx.substring(0, ii);\n\t\t\t} else {\n\t\t\t\ttheFormattedText += xx;\n\t\t\t}\n\t\t} else {\n\t\t\ttheFormattedText += xx;\n\t\t}\n\n\t\t// add repeat number\n\t\tif (Repeat.getRptNum(appt.getFrequency())) {\n\t\t\ttheFormattedText += \" (\" + Repeat.calculateRepeatNumber(day, appt) + \")\";\n\t\t}\n\n\t\treturn theFormattedText;\n\t}\n\n\t@Override\n\tpublic Collection<LabelEntity> search(SearchCriteria criteria) {\n\t\t\n\t\tCollection<LabelEntity> ret = new ArrayList<LabelEntity>();\n\t\tif( !show ) return ret;\n\n\t\t\n\t\tfor( HashMap<Integer, Collection<LabelEntity>> cal : calmap.values() ) {\n\t\t\tfor( Collection<LabelEntity> labels : cal.values()) {\n\t\t\t\tfor( LabelEntity label : labels ) {\n\t\t\t\t\t\n\t\t\t\t\t// filter by repeat\n\t\t\t\t\tif ( criteria.isTodo() || criteria.isVacation() || criteria.isHoliday() || criteria.hasLinks())\n\t\t\t\t\t\tcontinue;\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tif (!criteria.search(label.getTooltipText()))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\n\t\t\t\t\t// filter by start date\n\t\t\t\t\tif (criteria.getStartDate() != null) {\n\t\t\t\t\t\tif (label.getDate().before(criteria.getStartDate()))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// filter by end date\n\t\t\t\t\tif (criteria.getEndDate() != null) {\n\t\t\t\t\t\tif (label.getDate().after(criteria.getEndDate()))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tret.add(label);\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\treturn ret;\n\t}\n\t\n\tprivate String cacheFile() {\n\t\tString home = System.getProperty(\"user.home\", \"\");\n\t\treturn home + \"/\" + Prefs.getPref(PrefName.SUB_CACHE_FILENAME);\n\t}\n\t\n\tprivate void deleteCache() {\n\t\tFile c = new File(cacheFile());\n\t\tc.delete();\n\t}\n\t\n\tpublic void createCache() {\n\t\tdeleteCache();\n\t\tFile c = new File(cacheFile());\n\t\tGson gson = new Gson();\n\t\t\n\t\ttry (FileWriter writer = new FileWriter(c)) {\n\t\t    gson.toJson(calmap, writer);\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\t\n\tpublic void loadCache() {\n\t\tGson gson = new Gson();\n\t\tFile c = new File(cacheFile());\n\t\ttry (FileReader reader = new FileReader(c)) {\n\t\t    java.lang.reflect.Type mapType = new TypeToken<HashMap<String, HashMap<Integer, Collection<LabelEntity>>>>(){}.getType();\n\t\t    calmap = gson.fromJson(reader, mapType);\n\t\t} catch (FileNotFoundException e) {\n\t\t\t//e.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/SyncEvent.java",
    "content": "package net.sf.borg.model.sync;\n\nimport jakarta.xml.bind.annotation.XmlAccessType;\nimport jakarta.xml.bind.annotation.XmlAccessorType;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\nimport net.sf.borg.model.entity.SyncableEntity.ObjectType;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@XmlRootElement(name = \"SyncEvent\")\n@XmlAccessorType(XmlAccessType.FIELD)\npublic class SyncEvent {\n\n\t\n\n\tprivate Integer id;\n\tprivate String uid;\n\tprivate String url;\n\tprivate ChangeAction action;\n\tprivate ObjectType objectType;\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/SyncLog.java",
    "content": "package net.sf.borg.model.sync;\n\nimport java.io.InputStream;\nimport java.io.Writer;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport jakarta.xml.bind.JAXBContext;\nimport jakarta.xml.bind.Marshaller;\nimport jakarta.xml.bind.Unmarshaller;\nimport jakarta.xml.bind.annotation.XmlRootElement;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBUpgrader;\nimport net.sf.borg.model.entity.SyncableEntity;\nimport net.sf.borg.model.entity.SyncableEntity.ObjectType;\nimport net.sf.borg.model.sync.google.GCal;\n\n/**\n * class to track all appointment model changes since the last sync it will\n * persist this information to a file\n */\npublic class SyncLog extends Model implements Model.Listener, Prefs.Listener {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\n\tstatic private SyncLog singleton = null;\n\n\tprivate boolean processUpdates = true;\n\n\tstatic public SyncLog getReference() {\n\t\tif (singleton == null)\n\t\t\tsingleton = new SyncLog();\n\t\treturn singleton;\n\t}\n\n\tpublic SyncLog() {\n\n\t\tsetProcessUpdates(GCal.isSyncing());\n\n\t\tnew JdbcDBUpgrader(\"select id from syncmap\",\n\t\t\t\t\"CREATE TABLE IF NOT EXISTS syncmap (id integer NOT NULL,uid longvarchar, url longvarchar, objtype varchar(25) NOT NULL,action varchar(25) NOT NULL,PRIMARY KEY (id,objtype))\")\n\t\t\t\t\t\t.upgrade();\n\t\tnew JdbcDBUpgrader(\"select url from syncmap\",\n\t\t\t\t\"ALTER TABLE syncmap ADD url longvarchar\")\n\t\t\t\t\t\t.upgrade();\n\t\tAppointmentModel.getReference().addUnblockableListener(this);\n\t\tTaskModel.getReference().addUnblockableListener(this);\n\t\tPrefs.addListener(this);\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent borgEvent) {\n\n\t\tlog.fine(\"SyncLog update: \" + borgEvent.toString());\n\n\t\tif (!isProcessUpdates())\n\t\t\treturn;\n\n\t\tif (borgEvent == null || borgEvent.getObject() == null || borgEvent.getAction() == null)\n\t\t\treturn;\n\n\t\t//if( GCal.isSyncing() && !(borgEvent.getObject() instanceof Appointment))\n\t\t//\treturn;\n\n\t\ttry {\n\n\t\t\tObject obj = borgEvent.getObject();\n\t\t\tSyncEvent newEvent = new SyncEvent();\n\t\t\tif (obj instanceof SyncableEntity) {\n\t\t\t\tSyncableEntity se = (SyncableEntity) obj;\n\t\t\t\tnewEvent.setId(Integer.valueOf(se.getKey()));\n\t\t\t\tnewEvent.setObjectType(se.getObjectType());\n\t\t\t\tnewEvent.setUrl(se.getUrl());\n\t\t\t\tnewEvent.setUid(se.getUid());\n\n\t\t\t} else {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tnewEvent.setAction(borgEvent.getAction());\n\n\t\t\tInteger id = newEvent.getId();\n\t\t\tObjectType type = newEvent.getObjectType();\n\t\t\tSyncEvent existingEvent = get(id.intValue(), type);\n\t\t\tString uid = newEvent.getUid();\n\t\t\tString url = newEvent.getUrl();\n\n\t\t\t// any condition not listed is either a no-op or cannot occur\n\t\t\tif (existingEvent == null) {\n\t\t\t\tthis.insert(newEvent);\n\t\t\t} else {\n\n\t\t\t\tif (existingEvent.getAction() == ChangeEvent.ChangeAction.ADD\n\t\t\t\t\t\t&& newEvent.getAction() == ChangeEvent.ChangeAction.DELETE) {\n\t\t\t\t\tthis.delete(id.intValue(), type);\n\t\t\t\t} else if (existingEvent.getAction() == ChangeEvent.ChangeAction.CHANGE\n\t\t\t\t\t\t&& newEvent.getAction() == ChangeEvent.ChangeAction.DELETE) {\n\t\t\t\t\tSyncEvent event = new SyncEvent(id, uid, url, ChangeEvent.ChangeAction.DELETE, type);\n\t\t\t\t\tthis.delete(id.intValue(), type);\n\t\t\t\t\tthis.insert(event);\n\t\t\t\t} else if (existingEvent.getAction() == ChangeEvent.ChangeAction.DELETE\n\t\t\t\t\t\t&& newEvent.getAction() == ChangeEvent.ChangeAction.ADD) {\n\t\t\t\t\tSyncEvent event = new SyncEvent(id, uid, url, ChangeEvent.ChangeAction.CHANGE, type);\n\t\t\t\t\tthis.delete(id.intValue(), type);\n\t\t\t\t\tthis.insert(event);\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\tprivate static SyncEvent createFrom(ResultSet r) throws SQLException {\n\t\tint id = r.getInt(\"id\");\n\t\tChangeEvent.ChangeAction action = ChangeEvent.ChangeAction.valueOf(r.getString(\"action\"));\n\t\tString uid = r.getString(\"uid\");\n\t\tString url = r.getString(\"url\");\n\t\tString type = r.getString(\"objtype\");\n\t\tObjectType otype = ObjectType.valueOf(type);\n\t\tSyncEvent event = new SyncEvent(Integer.valueOf(id), uid, url, action, otype);\n\t\treturn event;\n\t}\n\n\tpublic SyncEvent get(int id, ObjectType type) throws Exception {\n\n\t\tSyncEvent ret = null;\n\n\t\tPreparedStatement stmt = DBHelper.getController().getConnection()\n\t\t\t\t.prepareStatement(\"SELECT * FROM syncmap WHERE id = ? and objtype = ?\");\n\t\tstmt.setInt(1, id);\n\t\tstmt.setString(2, type.toString());\n\n\t\tResultSet r = null;\n\t\ttry {\n\t\t\tr = stmt.executeQuery();\n\t\t\tif (r.next()) {\n\t\t\t\tret = createFrom(r);\n\t\t\t}\n\t\t\treturn ret;\n\t\t} finally {\n\t\t\tif (r != null)\n\t\t\t\tr.close();\n\t\t\tstmt.close();\n\t\t}\n\t}\n\n\tpublic List<SyncEvent> getAll() throws Exception {\n\n\t\tList<SyncEvent> ret = new ArrayList<SyncEvent>();\n\n\t\tPreparedStatement stmt = DBHelper.getController().getConnection().prepareStatement(\"SELECT * FROM syncmap\");\n\n\t\tResultSet r = null;\n\t\ttry {\n\t\t\tr = stmt.executeQuery();\n\t\t\twhile (r.next()) {\n\t\t\t\tret.add(createFrom(r));\n\t\t\t}\n\t\t\treturn ret;\n\t\t} finally {\n\t\t\tif (r != null)\n\t\t\t\tr.close();\n\t\t\tif (stmt != null)\n\t\t\t\tstmt.close();\n\t\t}\n\t}\n\n\tpublic void insert(SyncEvent event) throws Exception {\n\n\t\tlog.fine(\"SyncLog insert: \" + event.toString());\n\n\t\tPreparedStatement stmt = DBHelper.getController().getConnection()\n\t\t\t\t.prepareStatement(\"INSERT INTO syncmap ( id, uid, url, action, objtype) \" + \" VALUES \" + \"( ?, ?, ?, ?, ?)\");\n\n\t\tstmt.setInt(1, event.getId().intValue());\n\t\tstmt.setString(2, event.getUid());\n\t\tif( event.getUrl() != null)\n\t\t\tstmt.setString(3, event.getUrl());\n\t\telse\n\t\t\tstmt.setNull(3, Types.LONGVARCHAR);\n\t\tstmt.setString(4, event.getAction().toString());\n\t\tstmt.setString(5, event.getObjectType().toString());\n\t\tstmt.executeUpdate();\n\t\tstmt.close();\n\n\t\tthis.refreshListeners();\n\n\t}\n\n\tpublic void delete(int id, ObjectType type) throws Exception {\n\t\tPreparedStatement stmt = DBHelper.getController().getConnection()\n\t\t\t\t.prepareStatement(\"DELETE FROM syncmap WHERE id = ? and objtype = ?\");\n\n\t\tstmt.setInt(1, id);\n\t\tstmt.setString(2, type.toString());\n\t\tstmt.executeUpdate();\n\t\tstmt.close();\n\n\t\tthis.refreshListeners();\n\n\t}\n\n\tpublic void deleteAll() throws Exception {\n\t\tPreparedStatement stmt = DBHelper.getController().getConnection().prepareStatement(\"DELETE FROM syncmap\");\n\n\t\tstmt.executeUpdate();\n\t\tstmt.close();\n\n\t\tthis.refreshListeners();\n\n\t}\n\n\t@XmlRootElement(name = \"SYNCMAP\")\n\tprivate static class XmlContainer {\n\t\tpublic Collection<SyncEvent> SyncEvents;\n\t}\n\n\t@Override\n\tpublic void export(Writer fw) throws Exception {\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\n\t\tMarshaller m = jc.createMarshaller();\n\t\tXmlContainer container = new XmlContainer();\n\t\tcontainer.SyncEvents = getAll();\n\t\tm.marshal(container, fw);\n\t}\n\n\t@Override\n\tpublic void importXml(InputStream is) throws Exception {\n\t\tJAXBContext jc = JAXBContext.newInstance(XmlContainer.class);\n\t\tUnmarshaller u = jc.createUnmarshaller();\n\n\t\tXmlContainer container = (XmlContainer) u.unmarshal(is);\n\n\t\tif (container.SyncEvents == null)\n\t\t\treturn;\n\n\t\tfor (SyncEvent evt : container.SyncEvents) {\n\t\t\tinsert(evt);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic String getExportName() {\n\t\treturn \"SYNCMAP\";\n\t}\n\n\t@Override\n\tpublic String getInfo() throws Exception {\n\t\treturn \"Synclogs: \" + getAll().size();\n\t}\n\n\tpublic boolean isProcessUpdates() {\n\t\treturn processUpdates;\n\t}\n\n\tpublic void setProcessUpdates(boolean processUpdates) {\n\t\tthis.processUpdates = processUpdates;\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\n\t\tsetProcessUpdates(GCal.isSyncing());\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/google/DriveFileManager.java",
    "content": "package net.sf.borg.model.sync.google;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\n\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.model.File;\nimport com.google.api.services.drive.model.FileList;\n\n/*\n * written by google gemini\n */\n\npublic class DriveFileManager {\n\n    // The Google Drive service object, assumed to be properly initialized and authenticated.\n    private final Drive driveService;\n\n    public DriveFileManager(Drive driveService) {\n        this.driveService = driveService;\n    }\n    \n    // --- Core Public Method ---\n\n    /**\n     * Gets the file ID of a file by its full Drive path (e.g., \"FolderA/SubFolderB/myFile.txt\").\n     * If the file or any folder in the path does not exist, it is created.\n     *\n     * @param fullPath The full path of the file, starting from My Drive (root).\n     * @param mimeType The MIME type for the file if it needs to be created (e.g., \"text/plain\").\n     * @return The ID of the existing or newly created file.\n     * @throws IOException if any API call fails.\n     */\n    public String findOrCreateFile(String fullPath, String mimeType) throws IOException {\n        if (fullPath == null || fullPath.isEmpty()) {\n            throw new IllegalArgumentException(\"Path cannot be empty.\");\n        }\n\n        // 1. Separate file name from the path to its parent folder\n        String[] pathParts = fullPath.split(\"/\");\n        String fileName = pathParts[pathParts.length - 1];\n        \n        // 2. Find or create all parent folders in the path\n        String currentParentId = \"root\"; // Start from My Drive\n        for (int i = 0; i < pathParts.length - 1; i++) {\n            currentParentId = findOrCreateFolder(currentParentId, pathParts[i]);\n        }\n        \n        // 3. Search for the file in the final parent folder\n        File targetFile = searchFileInFolder(currentParentId, fileName);\n\n        // 4. Create the file if it doesn't exist\n        if (targetFile == null) {\n            System.out.println(\"File not found: \" + fullPath + \". Creating new file.\");\n            targetFile = createFileInFolder(currentParentId, fileName, mimeType);\n        } else {\n            System.out.println(\"File found: \" + fullPath + \" (ID: \" + targetFile.getId() + \")\");\n        }\n\n        return targetFile.getId();\n    }\n    \n    // --- Helper Methods ---\n    \n    /**\n     * Finds a file by name within a specific parent folder ID.\n     *\n     * @param parentId The ID of the folder to search in. Use \"root\" for My Drive.\n     * @param fileName The name of the file to search for.\n     * @return The File object if found, or null otherwise.\n     * @throws IOException if the API call fails.\n     */\n    private File searchFileInFolder(String parentId, String fileName) throws IOException {\n        String query = String.format(\n            \"name = '%s' and '%s' in parents and trashed = false\",\n            fileName.replace(\"'\", \"\\\\'\"), // Escape single quotes in the name\n            parentId\n        );\n        \n        FileList result = driveService.files().list()\n            .setQ(query)\n            .setFields(\"files(id, name)\") // Only request the ID and name\n            .execute();\n            \n        List<File> files = result.getFiles();\n        \n        // Return the first match (Drive allows multiple files with the same name, \n        // but this logic assumes you want the first one found).\n        return files.isEmpty() ? null : files.get(0);\n    }\n    \n    /**\n     * Creates a new file in the specified parent folder.\n     *\n     * @param parentId The ID of the folder where the file should be created.\n     * @param fileName The name of the new file.\n     * @param mimeType The MIME type of the new file.\n     * @return The newly created File object.\n     * @throws IOException if the API call fails.\n     */\n    private File createFileInFolder(String parentId, String fileName, String mimeType) throws IOException {\n        File fileMetadata = new File();\n        fileMetadata.setName(fileName);\n        fileMetadata.setMimeType(mimeType);\n        fileMetadata.setParents(Collections.singletonList(parentId));\n        \n        // Note: This creates an empty file. For a file with content, you'd use \n        // .setMediaContent() or .upload() depending on the method.\n        File newFile = driveService.files().create(fileMetadata)\n            .setFields(\"id, name\")\n            .execute();\n            \n        return newFile;\n    }\n    \n    /**\n     * Finds a folder by name within a parent ID, or creates it if it does not exist.\n     *\n     * @param parentId The ID of the parent folder. Use \"root\" for My Drive.\n     * @param folderName The name of the folder to find/create.\n     * @return The ID of the existing or newly created folder.\n     * @throws IOException if the API call fails.\n     */\n    private String findOrCreateFolder(String parentId, String folderName) throws IOException {\n        // 1. Search for the folder\n        String folderMimeType = \"application/vnd.google-apps.folder\";\n        String query = String.format(\n            \"name = '%s' and mimeType = '%s' and '%s' in parents and trashed = false\",\n            folderName.replace(\"'\", \"\\\\'\"),\n            folderMimeType,\n            parentId\n        );\n        \n        FileList result = driveService.files().list()\n            .setQ(query)\n            .setFields(\"files(id)\") // Only request the ID\n            .execute();\n\n        List<File> folders = result.getFiles();\n\n        if (!folders.isEmpty()) {\n            // Folder found\n            System.out.println(\"Folder found: \" + folderName + \" (ID: \" + folders.get(0).getId() + \")\");\n            return folders.get(0).getId();\n        }\n\n        // 2. Folder not found, create it\n        System.out.println(\"Folder not found: \" + folderName + \". Creating new folder.\");\n        File fileMetadata = new File();\n        fileMetadata.setName(folderName);\n        fileMetadata.setMimeType(folderMimeType);\n        fileMetadata.setParents(Collections.singletonList(parentId));\n\n        File newFolder = driveService.files().create(fileMetadata)\n            .setFields(\"id\")\n            .execute();\n\n        return newFolder.getId();\n    }\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/google/EntityGCalAdapter.java",
    "content": "package net.sf.borg.model.sync.google;\n\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TimeZone;\nimport java.util.UUID;\nimport java.util.logging.Logger;\n\nimport com.google.api.client.json.gson.GsonFactory;\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.tasks.model.Task;\n\nimport net.fortuna.ical4j.model.Recur;\nimport net.fortuna.ical4j.model.WeekDay;\nimport net.fortuna.ical4j.model.WeekDayList;\nimport net.sf.borg.common.ModalMessageServer;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.sync.RecurrenceRule;\n\npublic class EntityGCalAdapter {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\t// to create a new appt from a google created task\n\tpublic static Appointment toBorg(Task t) throws IOException {\n\t\tAppointment ap = AppointmentModel.getDefaultAppointment();\n\t\tif (ap == null)\n\t\t\tap = new Appointment();\n\n\t\tt.setFactory(new GsonFactory());\n\t\tap.setUrl(t.toPrettyString());\n\t\tap.setText(t.getTitle());\n\t\tap.setTodo(true);\n\t\tap.setUntimed(\"Y\");\n\t\tString d = t.getDue();\n\t\tif (d == null)\n\t\t\treturn null;\n\t\tDateTime dt = new DateTime(d);\n\t\tap.setDate(new Date(dt.getValue() - tzOffset(dt.getValue())));\n\n\t\t// look for aCalendar+ recurrence rule\n\t\tif (t.getNotes() != null) {\n\t\t\tint idx1 = t.getNotes().indexOf('[');\n\t\t\tint idx2 = t.getNotes().indexOf(']');\n\t\t\tif (idx1 != -1 && idx2 != -1) {\n\t\t\t\tString rrule = t.getNotes().substring(idx1 + 1, idx2);\n\t\t\t\tRecur recur;\n\t\t\t\ttry {\n\t\t\t\t\trecur = new Recur(rrule);\n\t\t\t\t\tRecurrenceRule.setRecur(ap, recur);\n\t\t\t\t} catch (ParseException e) {\n\t\t\t\t\tlogBoth(\"Could not parse recurrence: \" + rrule + \" ignoring for appt \" + ap.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ap;\n\n\t}\n\n\tpublic static Appointment toBorg(Event event) throws Exception {\n\n\t\t// start with default appt to pull in default options\n\t\tAppointment ap = AppointmentModel.getDefaultAppointment();\n\t\tif (ap == null)\n\t\t\tap = new Appointment();\n\n\t\t// For GCal - URL is used to store Event JSON\n\t\tevent.setFactory(new GsonFactory());\n\t\tap.setUrl(event.toPrettyString());\n\n\t\tap.setCategory(null);\n\n\t\tString appttext = event.getSummary();\n\t\tString summary = event.getSummary();\n\n\t\tif (event.getLocation() != null) {\n\t\t\tappttext += \"\\nLocation: \" + event.getLocation();\n\t\t}\n\n\t\tif (event.getDescription() != null) {\n\t\t\tappttext += \"\\n\" + event.getDescription();\n\t\t}\n\t\tap.setText(appttext);\n\n\t\tap.setUntimed(\"Y\");\n\n\t\tif (event.getStart().getDateTime() != null) {\n\t\t\tDate utc = new Date();\n\t\t\tutc.setTime(event.getStart().getDateTime().getValue());\n\t\t\tap.setDate(utc);\n\t\t\tap.setUntimed(\"N\");\n\t\t\tif (event.getEnd().getDateTime() != null) {\n\t\t\t\tlong dur = (event.getEnd().getDateTime().getValue() - utc.getTime())/(1000*60); // ms to minutes\n\t\t\t\tap.setDuration((int) dur);\n\t\t\t\t\n\t\t\t\t// if timed event spans days - just repeat it\n\t\t\t\tif( ap.getDuration() > 24*60 )\n\t\t\t\t{\n\t\t\t\t\t// timed appt > 1 day\n\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.DAILY);\n\t\t\t\t\tap.setRepeatFlag(true);\n\t\t\t\t\tap.setTimes(Math.floorDiv(ap.getDuration(),24*60)+1);\n\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tDate utc = new Date();\n\t\t\tutc.setTime(event.getStart().getDate().getValue() - tzOffset(event.getStart().getDate().getValue()));\n\t\t\t// utc.setTime(u);\n\t\t\tap.setDate(utc);\n\t\t\tap.setUntimed(\"Y\");\n\n\t\t}\n\n\t\tString uid = event.getICalUID();\n\n\t\tif (uid == null) {\n\t\t\tap.setUid(\"@NOUID-\" + UUID.randomUUID());\n\t\t} else {\n\t\t\tap.setUid(uid);\n\t\t}\n\n\t\tap.setCreateTime(new Date(event.getCreated().getValue()));\n\t\tap.setLastMod(new Date(event.getUpdated().getValue()));\n\n\t\tif (\"private\".equals(event.getVisibility()))\n\t\t\tap.setPrivate(true);\n\n\t\tif (event.getExtendedProperties() != null) {\n\t\t\tMap<String, String> props = event.getExtendedProperties().getPrivate();\n\n\t\t\tif (props.containsKey(\"holiday\"))\n\t\t\t\tap.setHoliday(Integer.valueOf(1));\n\t\t\tif (props.containsKey(\"private\"))\n\t\t\t\tap.setPrivate(true);\n\t\t\tif (props.containsKey(\"vacation\"))\n\t\t\t\tap.setVacation(Integer.valueOf(props.get(\"vacation\")));\n\t\t\tif (props.containsKey(\"color\"))\n\t\t\t\tap.setColor(props.get(\"color\"));\n\t\t\tif (props.containsKey(\"category\"))\n\t\t\t\tap.setCategory(props.get(\"category\"));\n\t\t}\n\n\t\tif (event.getRecurrence() != null && !event.getRecurrence().isEmpty())\n\n\t\t\tfor (String rl : event.getRecurrence()) {\n\n\t\t\t\tif (rl.startsWith(\"RRULE:\")) {\n\t\t\t\t\tRecur recur = new Recur(event.getRecurrence().get(0).substring(6));\n\n\t\t\t\t\tString freq = recur.getFrequency();\n\t\t\t\t\tint interval = recur.getInterval();\n\t\t\t\t\tif (freq.equals(Recur.DAILY)) {\n\t\t\t\t\t\tif (interval > 1) {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.NDAYS + \",\" + interval);\n\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.DAILY);\n\t\t\t\t\t} else if (freq.equals(Recur.WEEKLY)) {\n\t\t\t\t\t\tif (interval == 2) {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.BIWEEKLY);\n\t\t\t\t\t\t} else if (interval > 2) {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.NWEEKS + \",\" + interval);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.WEEKLY);\n\n\t\t\t\t\t\t\t// BORG can only handle daylist for weekly\n\t\t\t\t\t\t\tWeekDayList dl = recur.getDayList();\n\t\t\t\t\t\t\tif (dl != null && !dl.isEmpty()) {\n\t\t\t\t\t\t\t\tString f = net.sf.borg.model.Repeat.DAYLIST;\n\t\t\t\t\t\t\t\tf += \",\";\n\t\t\t\t\t\t\t\tfor (Object o : dl) {\n\t\t\t\t\t\t\t\t\tWeekDay wd = (WeekDay) o;\n\t\t\t\t\t\t\t\t\tf += WeekDay.getCalendarDay(wd);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tap.setFrequency(f);\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (freq.equals(Recur.MONTHLY)) {\n\t\t\t\t\t\tif (interval > 1) {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.NMONTHS + \",\" + interval);\n\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.MONTHLY);\n\t\t\t\t\t} else if (freq.equals(Recur.YEARLY)) {\n\t\t\t\t\t\tif (interval > 1) {\n\t\t\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.NYEARS + \",\" + interval);\n\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tap.setFrequency(Repeat.YEARLY);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.warning(\"WARNING: Cannot handle frequency of [\" + freq + \"], for appt [\" + summary\n\t\t\t\t\t\t\t\t+ \"], adding first occurrence only\\n\");\n\t\t\t\t\t\treturn ap;\n\t\t\t\t\t}\n\n\t\t\t\t\tDate until = recur.getUntil();\n\t\t\t\t\tif (until != null) {\n\t\t\t\t\t\tlong u = until.getTime() - tzOffset(until.getTime());\n\t\t\t\t\t\tap.setRepeatUntil(new Date(u));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint times = recur.getCount();\n\t\t\t\t\t\tif (times < 1)\n\t\t\t\t\t\t\ttimes = 9999;\n\t\t\t\t\t\tap.setTimes(Integer.valueOf(times));\n\t\t\t\t\t}\n\n\t\t\t\t\tap.setRepeatFlag(true);\n\t\t\t\t} else if (rl.startsWith(\"EXDATE\")) {\n\t\t\t\t\t// TODO\n\t\t\t\t\tlog.warning(\"skipping EXDATE: \" + rl);\t\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\telse {\n\t\t\t// check if non-repeating event spans multiple days. if so, convert to repeating daily\n\t\t\tif( ap.getDuration() > 24*60)\n\t\t\t{\n\t\t\t\t// timed appt > 1 day\n\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.DAILY);\n\t\t\t\tap.setRepeatFlag(true);\n\t\t\t\tap.setTimes(Math.floorDiv(ap.getDuration(),24*60)+1);\n\n\t\t\t}\n\t\t\telse if( ap.getDuration() == 0 && event.getStart().getDate() != null){\n\t\t\t\tint secs = (int) ((event.getEnd().getDate().getValue() - event.getStart().getDate().getValue())/1000);\n\t\t\t\tif( secs >= 24*60*60) {\n\t\t\t\t\tap.setFrequency(net.sf.borg.model.Repeat.DAILY);\n\t\t\t\t\tap.setRepeatFlag(true);\n\t\t\t\t\tap.setTimes(Math.floorDiv(secs,24*60*60));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ap;\n\t}\n\n\t// get the Event id from the Event JSON\n\tpublic static String getIdFromJSON(String json) {\n\n\t\tif (json == null)\n\t\t\treturn null;\n\t\tGsonFactory f = new GsonFactory();\n\t\tEvent e = null;\n\t\ttry {\n\t\t\te = f.fromString(json, Event.class);\n\t\t} catch (IOException ex) {\n\t\t\tex.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t\treturn e.getId();\n\n\t}\n\tpublic static String getKindFromJSON(String json) {\n\n\t\tif (json == null)\n\t\t\treturn null;\n\t\tGsonFactory f = new GsonFactory();\n\t\tEvent e = null;\n\t\ttry {\n\t\t\te = f.fromString(json, Event.class);\n\t\t} catch (IOException ex) {\n\t\t\tex.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t\treturn e.getKind();\n\n\t}\n\n\tpublic static String getIdFromTaskJSON(String json) {\n\n\t\tif (json == null)\n\t\t\treturn null;\n\t\tGsonFactory f = new GsonFactory();\n\t\tTask e = null;\n\t\ttry {\n\t\t\te = f.fromString(json, Task.class);\n\t\t} catch (IOException ex) {\n\t\t\tex.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t\treturn e.getId();\n\n\t}\n\n\tpublic static Event toGCalEvent(Appointment ap) {\n\t\tEvent ev = new Event();\n\t\tev.setKind(\"calendar#event\");\n\t\tev.setEventType(\"default\");\n\t\tev.setFactory(new GsonFactory());\n\n\t\t// Get original event if any and copy conserved fields\n\t\tif (ap.getUrl() != null) {\n\t\t\ttry {\n\t\t\t\tEvent orig = ev.getFactory().fromString(ap.getUrl(), Event.class);\n\t\t\t\tev.setId(orig.getId());\n\t\t\t\tev.setKind(orig.getKind());\n\t\t\t\tev.setEventType(orig.getEventType());\n\t\t\t\tev.setEtag(orig.getEtag());\n\t\t\t\tev.setSequence(orig.getSequence());\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\tif (ap.isTodo())\n\t\t\treturn null;\n\t\tString uidval = ap.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = ap.getKey() + \"@BORGA-\" + ap.getCreateTime().getTime();\n\t\t}\n\t\tev.setICalUID(uidval);\n\n\t\tev.setCreated(new DateTime(ap.getCreateTime()));\n\t\tev.setUpdated(new DateTime(ap.getLastMod()));\n\t\t\n\t\tev.setSummary(ap.getTitle());\n\t\tev.setDescription(ap.getBody());\n\n\t\tEventDateTime dt = new EventDateTime();\n\t\tif (AppointmentModel.isNote(ap)) {\n\t\t\tdt.setDate(new DateTime(true, ap.getDate().getTime(), 0));\n\t\t} else {\n\t\t\tdt.setDateTime(new DateTime(ap.getDate()));\n\t\t\tdt.setTimeZone(\"America/New_York\");\n\t\t}\n\n\t\tev.setStart(dt);\n\n\t\tEventDateTime edt = new EventDateTime();\n\t\tif (!AppointmentModel.isNote(ap) && ap.getDuration() != null && ap.getDuration().intValue() != 0) {\n\t\t\tedt.setDateTime(new DateTime(ap.getDate().getTime() + 1000 * 60 * ap.getDuration().intValue()));\n\t\t\tedt.setTimeZone(\"America/New_York\");\n\t\t} else {\n\t\t\tedt.setDate(new DateTime(true, ap.getDate().getTime() + 1000 * 60 * 30, 0));\n\t\t}\n\n\t\tev.setEnd(edt);\n\n\t\t// TODO categories, holiday, vacation, color\n\t\tMap<String, String> propmap = new HashMap<String, String>();\n\n\t\tif (ap.getVacation() != null) {\n\t\t\tpropmap.put(\"vacation\", Integer.toString(ap.getVacation()));\n\t\t}\n\n\t\t// holiday is a category\n\t\tif (ap.getHoliday() != null && ap.getHoliday().intValue() != 0) {\n\t\t\tpropmap.put(\"holiday\", \"true\");\n\t\t}\n\n\t\t// private\n\t\tif (ap.isPrivate()) {\n\t\t\tpropmap.put(\"private\", \"true\");\n\t\t\tev.setVisibility(\"private\");\n\t\t}\n\n\t\t// add color as a category\n\t\tif (ap.getColor() != null) {\n\t\t\tpropmap.put(\"color\", ap.getColor());\n\t\t}\n\n\t\tif (ap.getCategory() != null && !ap.getCategory().equals(\"\")) {\n\t\t\tpropmap.put(\"category\", ap.getCategory());\n\t\t}\n\n\t\tif (ev.getExtendedProperties() == null)\n\t\t\tev.setExtendedProperties(new Event.ExtendedProperties());\n\t\tev.getExtendedProperties().setPrivate(propmap);\n\n\t\tif (ap.isRepeatFlag())\n\t\t\tev.setRecurrence(List.of(\"RRULE:\" + RecurrenceRule.getRRule(ap)));\n\n\t\treturn ev;\n\t}\n\n\tstatic private int tzOffset(long date) {\n\t\treturn TimeZone.getDefault().getOffset(date);\n\t}\n\n\tpublic static Task toGCalTask(Appointment appt) {\n\n\t\tif (!appt.isTodo())\n\t\t\treturn null;\n\t\t\n\t\t\n\n\t\tTask task = new Task();\n\t\ttask.setKind(\"tasks#task\");\n\t\ttask.setTitle(appt.getTitle());\n\t\tString notes = appt.getBody();\n\t\t\n\t\ttask.setFactory(new GsonFactory());\n\n\t\tif (appt.getUrl() != null) {\n\t\t\ttry {\n\t\t\t\tTask orig = task.getFactory().fromString(appt.getUrl(), Task.class);\n\t\t\t\ttask.setId(orig.getId());\n\t\t\t\ttask.setKind(orig.getKind());\n\t\t\t\ttask.setEtag(orig.getEtag());\n\t\t\t\ttask.setStatus(orig.getStatus());\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\tDate nt = appt.getNextTodo();\n\t\tif (nt == null) {\n\t\t\tnt = appt.getDate();\n\t\t}\n\t\tDateTime due = new DateTime(nt);\n\n\t\ttask.setDue(due.toStringRfc3339());\n\n\t\tString uidval = appt.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = appt.getKey() + \"@BORGA-\" + appt.getCreateTime().getTime();\n\t\t}\n\n\t\tif (appt.isRepeatFlag()) {\n\t\t\tnotes += \" [\" + RecurrenceRule.getRRule(appt) + \"]\";\n\t\t}\n\n\t\tnotes += \" UID:\" + uidval;\n\n\t\ttask.setNotes(notes);\n\n\t\treturn task;\n\t}\n\n\tpublic static Task toGCalTask(net.sf.borg.model.entity.Task t) throws Exception {\n\n\t\tif (TaskModel.isClosed(t))\n\t\t\treturn null;\n\n\t\tTask task = new Task();\n\t\ttask.setKind(\"tasks#task\");\n\t\ttask.setTitle(t.getText());\n\t\ttask.setFactory(new GsonFactory());\n\n\t\tif (t.getUrl() != null) {\n\t\t\ttry {\n\t\t\t\tTask orig = task.getFactory().fromString(t.getUrl(), Task.class);\n\t\t\t\ttask.setId(orig.getId());\n\t\t\t\ttask.setKind(orig.getKind());\n\t\t\t\ttask.setEtag(orig.getEtag());\n\t\t\t\ttask.setStatus(orig.getStatus());\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\tDate due = t.getDueDate();\n\t\tif (due != null) {\n\t\t\tDateTime tdue = new DateTime(false,due.getTime(), 0);\n\t\t\ttask.setDue(tdue.toStringRfc3339());\n\t\t}\n\n\t\tString uidval = t.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = t.getKey() + \"@BORGT-\" + t.getCreateTime().getTime();\n\t\t}\n\n\t\tStringBuffer sb = new StringBuffer();\n\t\tif (t.getDescription() != null) {\n\t\t\tsb.append(\"\\n\" + t.getDescription() + \"\\n\");\n\t\t}\n\t\tif (t.getResolution() != null) {\n\t\t\tsb.append(\"\\n\" + t.getResolution() + \"\\n\");\n\t\t}\n\t\tfor (Subtask subtask : TaskModel.getReference().getSubTasks(t.getKey())) {\n\t\t\tif (subtask.getCloseDate() == null) {\n\t\t\t\tsb.append(\"\\n\" + subtask.getText());\n\t\t\t\tif (subtask.getDueDate() != null)\n\t\t\t\t\tsb.append(\"  \" + subtask.getDueDate());\n\t\t\t\tsb.append(\"\\n\");\n\t\t\t}\n\t\t}\n\n\t\tsb.append(\"\\n\" + \" UID:\" + uidval);\n\n\t\tString notes = sb.toString();\n\n\t\ttask.setNotes(notes);\n\n\t\treturn task;\n\n\t}\n\n\tpublic static Task toGCalTask(net.sf.borg.model.entity.Subtask s) {\n\t\tif (s.getCloseDate() != null)\n\t\t\treturn null;\n\n\t\tDate due = s.getDueDate();\n\t\tif (due == null)\n\t\t\treturn null;\n\n\t\tTask task = new Task();\n\t\ttask.setKind(\"tasks#task\");\n\t\ttask.setTitle(s.getText());\n\t\ttask.setFactory(new GsonFactory());\n\n\t\tif (s.getUrl() != null) {\n\t\t\ttry {\n\t\t\t\tTask orig = task.getFactory().fromString(s.getUrl(), Task.class);\n\t\t\t\ttask.setId(orig.getId());\n\t\t\t\ttask.setKind(orig.getKind());\n\t\t\t\ttask.setEtag(orig.getEtag());\n\t\t\t\ttask.setStatus(orig.getStatus());\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\tDateTime tdue = new DateTime(false,due.getTime(), 0);\n\t\ttask.setDue(tdue.toStringRfc3339());\n\n\t\tString uidval = s.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = s.getKey() + \"@BORGS-\" + s.getCreateTime().getTime();\n\t\t}\n\n\t\tString notes = \" UID:\" + uidval;\n\n\t\ttask.setNotes(notes);\n\n\t\treturn task;\n\n\t}\n\n\t// log to both logfile and SYNC popup\n\tstatic private void logBoth(String s) {\n\t\tlog.info(s);\n\t\tModalMessageServer.getReference().sendLogMessage(s);\n\t}\n\t\n\t// create a simple dummy event to show a todo on a \"special\" google calendar\n\tpublic static Event toGCalDummyEvent(CalendarEntity ap) {\n\t\tEvent ev = new Event();\n\t\tev.setKind(\"calendar#event\");\n\t\tev.setEventType(\"default\");\n\t\tev.setFactory(new GsonFactory());\n\n\t\tev.setCreated(new DateTime(ap.getCreateTime()));\n\t\tev.setUpdated(new DateTime(ap.getLastMod()));\n\t\t\n\t\tev.setSummary(ap.getTitle());\n\t\tev.setDescription(ap.getBody());\n\t\t\n\t\tDate nt = ap.getNextTodo();\n\t\tif (nt == null) {\n\t\t\tnt = ap.getDate();\n\t\t}\n\n\t\tEventDateTime dt = new EventDateTime();\n\t\tdt.setDate(new DateTime(true, nt.getTime(), 0));\n\t\tev.setStart(dt);\n\t\t\n\t\tEventDateTime edt = new EventDateTime();\n\t\tedt.setDate(new DateTime(true, nt.getTime() + 1000 * 60 * 30, 0));\n\t\tev.setEnd(edt);\n\n\n\t\t// TODO categories, holiday, vacation, color\n\t\tMap<String, String> propmap = new HashMap<String, String>();\n\n\t\t// private\n\t\tif (ap.isPrivate()) {\n\t\t\tpropmap.put(\"private\", \"true\");\n\t\t\tev.setVisibility(\"private\");\n\t\t}\n\n\t\tif (ev.getExtendedProperties() == null)\n\t\t\tev.setExtendedProperties(new Event.ExtendedProperties());\n\t\tev.getExtendedProperties().setPrivate(propmap);\n\n\n\t\treturn ev;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/google/FileDownloader.java",
    "content": "package net.sf.borg.model.sync.google;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.client.http.HttpResponseException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.file.Path;\nimport java.util.logging.Logger;\n\n/*\n * written by google gemini\n */\n\npublic class FileDownloader {\n\t\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\n    /**\n     * Downloads a Google Drive file given its ID.\n     *\n     * @param driveService The authenticated Drive service object.\n     * @param fileId The ID of the file to download.\n     * @param localFilePath The path to save the downloaded file locally.\n     * @throws IOException If an error occurs during API call or file writing.\n     */\n    public void downloadFile(Drive driveService, String fileId, Path localFilePath) throws IOException {\n        \n        log.info(\"Attempting to download file ID: \" + fileId + \" to path: \" + localFilePath);\n\n        // Handle Google Docs, Sheets, Slides, etc. (Google native formats)\n        // These files cannot be downloaded directly via 'files().get()'. \n        // They must be exported using 'files().export()'.\n        // For simplicity, this example assumes a non-native file (e.g., PDF, image, text).\n        // If you need to handle Google native files, you'll need a separate export function.\n        // The Drive API documentation can tell you the appropriate MIME type for the export.\n        \n        try (OutputStream outputStream = new FileOutputStream(localFilePath.toFile())) {\n            \n            // 1. Execute the download request using files().get()\n            driveService.files().get(fileId)\n                // 2. Download the file content and write it directly to the output stream\n                .executeMediaAndDownloadTo(outputStream); \n\n            log.info(\"Successfully downloaded file to: \" + localFilePath);\n\n        } catch (HttpResponseException e) {\n            log.info(\"Error downloading file. Status Code: \" + e.getStatusCode());\n            log.info(\"Reason: \" + e.getStatusMessage());\n            throw e; // Re-throw the exception for proper handling\n        }\n    }\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/google/GCal.java",
    "content": "package net.sf.borg.model.sync.google;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.List;\nimport java.util.TimeZone;\nimport java.util.logging.Logger;\n\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;\nimport com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.DateTime;\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.calendar.model.CalendarList;\nimport com.google.api.services.calendar.model.CalendarListEntry;\nimport com.google.api.services.calendar.model.Event;\nimport com.google.api.services.calendar.model.Events;\nimport com.google.api.services.tasks.Tasks;\nimport com.google.api.services.tasks.TasksScopes;\nimport com.google.api.services.tasks.model.Task;\nimport com.google.api.services.tasks.model.TaskList;\nimport com.google.api.services.tasks.model.TaskLists;\nimport com.google.gson.stream.MalformedJsonException;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.LogViewer.LogEntry;\nimport net.sf.borg.common.ModalMessageServer;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.SyncableEntity;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.model.sync.SyncEvent;\nimport net.sf.borg.model.sync.SyncLog;\n\npublic class GCal {\n\n\tprivate static final String APPLICATION_NAME = \"BORG Calendar\";\n\n\tprivate static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();\n\n\tprivate static final List<String> SCOPES = List.of(CalendarScopes.CALENDAR, TasksScopes.TASKS);\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tstatic volatile private GCal singleton = null;\n\n\tprivate String calendarId;\n\tprivate String todoCalId;\n\tprivate String taskList;\n\tprivate Calendar service = null;\n\tprivate Tasks tservice = null;\n\tprivate Collection<String> subscribed = new ArrayList<String>();\n\n\tprivate List<LogEntry> logEntries = new ArrayList<LogEntry>();\n\n\tpublic List<LogEntry> getLogEntries() {\n\t\treturn logEntries;\n\t}\n\n\tpublic static boolean isSyncing() {\n\t\treturn Prefs.getBoolPref(PrefName.GOOGLE_SYNC);\n\t}\n\n\tstatic public GCal getReference() {\n\t\tif (singleton == null) {\n\t\t\tGCal b = new GCal();\n\t\t\tsingleton = b;\n\t\t}\n\t\treturn (singleton);\n\t}\n\n\tstatic private int tzOffset(long date) {\n\t\treturn TimeZone.getDefault().getOffset(date);\n\t}\n\n\tprivate Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws Exception {\n\t\t// Load client secrets.\n\t\tFile f = new File(Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\t\t// InputStream in =\n\t\t// CalendarQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);\n\t\ttry {\n\t\t\tInputStream in = new FileInputStream(f);\n\n\t\t\tGoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));\n\n\t\t\t// Build flow and trigger user authorization request.\n\t\t\tGoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,\n\t\t\t\t\tclientSecrets, SCOPES)\n\t\t\t\t\t.setDataStoreFactory(\n\t\t\t\t\t\t\tnew FileDataStoreFactory(new java.io.File(Prefs.getPref(PrefName.GOOGLE_TOKEN_DIR))))\n\t\t\t\t\t.setAccessType(\"offline\").build();\n\t\t\tLocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();\n\t\t\tCredential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize(\"user\");\n\t\t\t// returns an authorized Credential object.\n\t\t\treturn credential;\n\t\t} catch (FileNotFoundException fe) {\n\t\t\tthrow new Exception(\"Credentials File not found: \" + Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\t\t} catch (MalformedJsonException mj) {\n\t\t\tlog.severe(mj.toString());\n\t\t\tthrow new Exception(\"could not parse JSON credentials file: \" + Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\t\t}\n\n\t}\n\n\t// null out the google ids so that they are fetched again\n\tpublic void resetGoogleIds() {\n\t\tcalendarId = null;\n\t\ttaskList = null;\n\t\ttodoCalId = null;\n\t}\n\n\tpublic void connect() throws Exception {\n\n\t\tif (service != null)\n\t\t\treturn;\n\n\t\t// Build a new authorized API client service.\n\t\tfinal NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n\t\tservice = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n\t\t\t\t.setApplicationName(APPLICATION_NAME).build();\n\n\t\ttservice = new Tasks.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n\t\t\t\t.setApplicationName(APPLICATION_NAME).build();\n\n\t}\n\n\t/**\n\t * Remove the sync relationship of all borg records by deleting the saved google\n\t * json and regenrating all UIDs\n\t * \n\t * @throws Exception\n\t */\n\tpublic void unsync() throws Exception {\n\n\t\t// delete all URL fields and regenerate the UIDs\n\t\tfor (Appointment ap : AppointmentModel.getReference().getAllAppts()) {\n\t\t\tif (ap.getUrl() != null || ap.getUid() != null) {\n\t\t\t\tap.setUrl(null);\n\t\t\t\tap.setUid(null);\n\t\t\t\tAppointmentModel.getReference().saveAppt(ap, false);\n\t\t\t}\n\t\t}\n\n\t\t// questionable implementation to get around code in save() that preserves url\n\t\t// even when nulled out in object\n\t\tDBHelper.getController().execSQL(\"update tasks set url = null\");\n\t\tDBHelper.getController().execSQL(\"update subtasks set url = null\");\n\n\t\tfor (net.sf.borg.model.entity.Task ap : TaskModel.getReference().getTasks()) {\n\t\t\tif (ap.getUrl() != null || ap.getUid() != null) {\n\t\t\t\tap.setUrl(null);\n\t\t\t\tap.setUid(null);\n\t\t\t\tTaskModel.getReference().savetask(ap, false);\n\t\t\t}\n\t\t}\n\t\tfor (Subtask ap : TaskModel.getReference().getSubTasks()) {\n\t\t\tif (ap.getUrl() != null || ap.getUid() != null) {\n\t\t\t\tap.setUrl(null);\n\t\t\t\tap.setUid(null);\n\t\t\t\tTaskModel.getReference().saveSubTask(ap, false);\n\t\t\t}\n\t\t}\n\n\t\t// empty the sync log\n\t\tSyncLog.getReference().deleteAll();\n\n\t}\n\n\tpublic synchronized void sync(Integer years, boolean fullsync, boolean cleanup) throws Exception {\n\n\t\tlogEntries.clear();\n\n\t\tDate after = null;\n\t\tGregorianCalendar gcal = new GregorianCalendar();\n\n\t\tgcal.add(java.util.Calendar.YEAR, -1 * ((years == null) ? 50 : years.intValue()));\n\t\tafter = gcal.getTime();\n\n\t\tlog.info(\" Connect\");\n\t\tconnect();\n\t\tsetIds();\n\n\t\tif (fullsync) {\n\t\t\t// get all appointments and add to syncmap\n\t\t\tfor (Appointment ap : AppointmentModel.getReference().getAllAppts()) {\n\n\t\t\t\t// limit by date\n\t\t\t\tDate latestInstance = Repeat.calculateLastRepeat(ap);\n\t\t\t\tif (latestInstance != null && latestInstance.before(after))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// never been synced - so force a new entry if none exists\n\t\t\t\tif (SyncLog.getReference().get(ap.getKey(), SyncableEntity.ObjectType.APPOINTMENT) == null) {\n\t\t\t\t\tSyncEvent event = new SyncEvent();\n\t\t\t\t\tevent.setId(ap.getKey());\n\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.ADD);\n\t\t\t\t\tevent.setObjectType(SyncableEntity.ObjectType.APPOINTMENT);\n\t\t\t\t\tevent.setUid(ap.getUid());\n\t\t\t\t\tif (ap.getUrl() != null) {\n\t\t\t\t\t\tevent.setUrl(ap.getUrl());\n\t\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.CHANGE);\n\t\t\t\t\t}\n\t\t\t\t\tSyncLog.getReference().insert(event);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tfor (net.sf.borg.model.entity.Task ap : TaskModel.getReference().getTasks()) {\n\t\t\t\tif (SyncLog.getReference().get(ap.getKey(), SyncableEntity.ObjectType.TASK) == null) {\n\t\t\t\t\tSyncEvent event = new SyncEvent();\n\t\t\t\t\tevent.setId(ap.getKey());\n\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.ADD);\n\t\t\t\t\tevent.setObjectType(SyncableEntity.ObjectType.TASK);\n\t\t\t\t\tevent.setUid(ap.getUid());\n\t\t\t\t\tif (ap.getUrl() != null) {\n\t\t\t\t\t\tevent.setUrl(ap.getUrl());\n\t\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.CHANGE);\n\t\t\t\t\t}\n\t\t\t\t\tSyncLog.getReference().insert(event);\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tfor (Subtask ap : TaskModel.getReference().getSubTasks()) {\n\t\t\t\tif (SyncLog.getReference().get(ap.getKey(), SyncableEntity.ObjectType.SUBTASK) == null) {\n\t\t\t\t\tSyncEvent event = new SyncEvent();\n\t\t\t\t\tevent.setId(ap.getKey());\n\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.ADD);\n\t\t\t\t\tevent.setObjectType(SyncableEntity.ObjectType.SUBTASK);\n\t\t\t\t\tevent.setUid(ap.getUid());\n\t\t\t\t\tif (ap.getUrl() != null) {\n\t\t\t\t\t\tevent.setUrl(ap.getUrl());\n\t\t\t\t\t\tevent.setAction(Model.ChangeEvent.ChangeAction.CHANGE);\n\t\t\t\t\t}\n\t\t\t\t\tSyncLog.getReference().insert(event);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tprocessSyncMap();\n\n\t\tsyncFromServer(after, cleanup);\n\n\t\t// incoming sync could cause additional outward activity due to borg\n\t\t// needing to convert multiple events\n\t\t// into one - a limitation of borg\n\t\tprocessSyncMap();\n\n\t\tsyncSubscribed(after);\n\n\t\tsyncTodoCalendar();\n\n\t\tlog.info(\" Done\");\n\t}\n\n\tpublic void setIds() throws Exception {\n\n\t\tString calname = Prefs.getPref(PrefName.GCAL_CAL_ID);\n\t\tString todoCalname = Prefs.getPref(PrefName.GCAL_TODO_CAL_ID);\n\t\tString taskname = Prefs.getPref(PrefName.GCAL_TASKLIST_ID);\n\t\tsubscribed.clear();\n\n\t\tCalendarList cals = service.calendarList().list().execute();\n\t\tlog.fine(cals.toPrettyString());\n\n\t\tString subs = Prefs.getPref(PrefName.GOOGLE_SUBSCRIBED);\n\t\tif (subs != null && !subs.isEmpty()) {\n\t\t\tList<String> subList = Arrays.asList(subs.split(\",\"));\n\t\t\tfor (String s : subList) {\n\t\t\t\tfor (CalendarListEntry c : cals.getItems()) {\n\t\t\t\t\tif (s.equals(c.getSummary()) || s.equals(c.getId())) {\n\t\t\t\t\t\tsubscribed.add(c.getId());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (calendarId == null) {\n\t\t\tfor (CalendarListEntry c : cals.getItems()) {\n\t\t\t\tif (calname.equals(c.getSummary()) || calname.equals(c.getId())) {\n\t\t\t\t\tcalendarId = c.getId();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tif (todoCalId == null && todoCalname != null && !todoCalname.isEmpty()) {\n\t\t\tfor (CalendarListEntry c : cals.getItems()) {\n\t\t\t\tif (todoCalname.equals(c.getSummary()) || todoCalname.equals(c.getId())) {\n\t\t\t\t\ttodoCalId = c.getId();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tif (taskList == null) {\n\t\t\tTaskLists result = tservice.tasklists().list().execute();\n\t\t\tList<TaskList> taskLists = result.getItems();\n\n\t\t\tif (taskLists != null) {\n\t\t\t\tfor (TaskList tasklist : taskLists) {\n\t\t\t\t\tlog.fine(\"TaskList Entry: \" + tasklist.getTitle() + \" : \" + tasklist.getId());\n\t\t\t\t\tif (taskname.equals(tasklist.getTitle())) {\n\t\t\t\t\t\ttaskList = tasklist.getId();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (calendarId == null)\n\t\t\tthrow new Exception(\"Could not determine calender id matching: \" + calname);\n\t\tif (taskList == null)\n\t\t\tthrow new Exception(\"Could not determine task list id matching: \" + taskname);\n\t}\n\n\tprivate void processSyncMap() throws Exception {\n\n\t\tList<SyncEvent> syncEvents = SyncLog.getReference().getAll();\n\n\t\tint num_outgoing = syncEvents.size();\n\n\t\tlogEntry(\" Process \" + num_outgoing + \" Outgoing Items\");\n\n\t\tfor (SyncEvent se : syncEvents) {\n\t\t\ttry {\n\t\t\t\tif (se.getObjectType() == SyncableEntity.ObjectType.APPOINTMENT) {\n\n\t\t\t\t\tif (se.getAction().equals(Model.ChangeEvent.ChangeAction.ADD)) {\n\t\t\t\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(se.getId());\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tif (ap.isTodo()) {\n\t\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(ap);\n\t\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\t\tif (t.getEtag() == null)\n\t\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tEvent ve1 = EntityGCalAdapter.toGCalEvent(ap);\n\t\t\t\t\t\t\t\tif (ve1 != null)\n\t\t\t\t\t\t\t\t\taddEvent(ve1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.CHANGE)) {\n\n\t\t\t\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(se.getId());\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tif (ap.isTodo()) {\n\t\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(ap);\n\t\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\t\tif (t.getEtag() == null) {\n\t\t\t\t\t\t\t\t\t\t// suspicious case - provide warning\n\t\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.WARN,\"Todo was changed in BORG that has no record of google task. Please check google. Manual delete may be needed\\n\" + ap.toString());\n\t\t\t\t\t\t\t\t\t\t//logEntry(ap.toString());\n\t\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t// check if non-todo changing to todo\n\t\t\t\t\t\t\t\t\t\tString kind = EntityGCalAdapter.getKindFromJSON(se.getUrl());\n\t\t\t\t\t\t\t\t\t\tif (kind != null && kind.contains(\"event\")) {\n\t\t\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.WARN,\"Event changed to Todo - removing from google calendar and adding to google tasks\\n\" + ap.toString());\n\t\t\t\t\t\t\t\t\t\t\t//logEntry(ap.toString());\n\t\t\t\t\t\t\t\t\t\t\t// need to delete event from google calendar and add new task\n\t\t\t\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromJSON(se.getUrl());\n\t\t\t\t\t\t\t\t\t\t\tif (id == null)\n\t\t\t\t\t\t\t\t\t\t\t\tid = se.getUid();\n\t\t\t\t\t\t\t\t\t\t\tEvent comp = getEvent(id);\n\n\t\t\t\t\t\t\t\t\t\t\tif (comp != null) {\n\t\t\t\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.UPDATE,\" removeEvent: \" + comp);\n\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\tremoveEvent(comp.getId());\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.ERROR,\" ERROR for: \" + se + \":\" + e.getMessage());\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t// null out url (google json) so that this will updated with the new task\n\t\t\t\t\t\t\t\t\t\t\t// json\n\t\t\t\t\t\t\t\t\t\t\tap.setUrl(null);\n\t\t\t\t\t\t\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t\t\t\t\t\t\t\tt = EntityGCalAdapter.toGCalTask(ap);\n\t\t\t\t\t\t\t\t\t\t\taddTask(t);\n\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else { // non-todo being changed\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromJSON(se.getUrl());\n\t\t\t\t\t\t\t\tString kind = EntityGCalAdapter.getKindFromJSON(se.getUrl());\n\n\t\t\t\t\t\t\t\tEvent ev = EntityGCalAdapter.toGCalEvent(ap);\n\n\t\t\t\t\t\t\t\tif (kind != null && kind.contains(\"task\")) {\n\t\t\t\t\t\t\t\t\t// task changing to event\n\t\t\t\t\t\t\t\t\t// delete task and then add new event\n\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.WARN,\"Todo changed to Event - removing from google tasks and adding to google calendar\\n\" + ap.toString());\n\t\t\t\t\t\t\t\t\tString tid = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\t\tif (tid != null)\n\t\t\t\t\t\t\t\t\t\tremoveTask(tid);\n\n\t\t\t\t\t\t\t\t\t// null out url (google json) so that this will updated with the new event json\n\t\t\t\t\t\t\t\t\tap.setUrl(null);\n\t\t\t\t\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t\t\t\t\t\tev = EntityGCalAdapter.toGCalEvent(ap);\n\t\t\t\t\t\t\t\t\taddEvent(ev);\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tif (id == null)\n\t\t\t\t\t\t\t\t\t\tid = se.getUid();\n\t\t\t\t\t\t\t\t\tEvent comp = getEvent(id);\n\n\t\t\t\t\t\t\t\t\tif (comp == null) {\n\t\t\t\t\t\t\t\t\t\tif (ev != null)\n\t\t\t\t\t\t\t\t\t\t\taddEvent(ev);\n\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (ev != null)\n\t\t\t\t\t\t\t\t\t\t\tupdateEvent(ev);\n\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.DELETE)) {\n\n\t\t\t\t\t\tif (se.getUrl() != null) {\n\t\t\t\t\t\t\tif (se.getUrl().contains(\"tasks#task\")) {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id != null)\n\t\t\t\t\t\t\t\t\tremoveTask(id);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id == null)\n\t\t\t\t\t\t\t\t\tid = se.getUid();\n\t\t\t\t\t\t\t\tEvent comp = getEvent(id);\n\n\t\t\t\t\t\t\t\tif (comp != null) {\n\t\t\t\t\t\t\t\t\tlog.info(\" removeEvent: \" + comp);\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tremoveEvent(comp.getId());\n\t\t\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.ERROR,\" ERROR for: \" + se + \":\" + e.getMessage());\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlogEntry(\"Deleted Appt: \" + se.getUid() + \" not found on server\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telse if (se.getObjectType() == SyncableEntity.ObjectType.TASK) {\n\n\t\t\t\t\tif (se.getAction().equals(Model.ChangeEvent.ChangeAction.ADD)) {\n\t\t\t\t\t\tnet.sf.borg.model.entity.Task task = TaskModel.getReference().getTask(se.getId());\n\t\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(task);\n\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\tif (t.getEtag() == null)\n\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.CHANGE)) {\n\n\t\t\t\t\t\tnet.sf.borg.model.entity.Task task = TaskModel.getReference().getTask(se.getId());\n\t\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(task);\n\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\tif (t.getEtag() == null) {\n\t\t\t\t\t\t\t\t\t// suspicious case - provide warning\n\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.WARN,\"Task was changed in BORG that has no record of google task. Please check google. Manual delete may be needed.\" + task.toString());\n\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id != null)\n\t\t\t\t\t\t\t\t\tremoveTask(id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.DELETE)) {\n\n\t\t\t\t\t\tif (se.getUrl() != null) {\n\t\t\t\t\t\t\tif (se.getUrl().contains(\"tasks#task\")) {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id != null)\n\t\t\t\t\t\t\t\t\tremoveTask(id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\telse if (se.getObjectType() == SyncableEntity.ObjectType.SUBTASK) {\n\n\t\t\t\t\tif (se.getAction().equals(Model.ChangeEvent.ChangeAction.ADD)) {\n\t\t\t\t\t\tnet.sf.borg.model.entity.Subtask task = TaskModel.getReference().getSubTask(se.getId());\n\t\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(task);\n\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\tif (t.getEtag() == null)\n\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.CHANGE)) {\n\n\t\t\t\t\t\tnet.sf.borg.model.entity.Subtask task = TaskModel.getReference().getSubTask(se.getId());\n\t\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t\tTask t = EntityGCalAdapter.toGCalTask(task);\n\t\t\t\t\t\t\tif (t != null) {\n\t\t\t\t\t\t\t\t// check if task exists in google already\n\t\t\t\t\t\t\t\tif (t.getEtag() == null) {\n\t\t\t\t\t\t\t\t\t// suspicious case - provide warning\n\t\t\t\t\t\t\t\t\tlogEntry(LogEntry.WARN,\"Subtask was changed in BORG that has no record of google task. Please check google. Manual delete may be needed.\\n\" + task.toString());\n\t\t\t\t\t\t\t\t\taddTask(t);\n\t\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\t\t\tupdateTask(t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id != null)\n\t\t\t\t\t\t\t\t\tremoveTask(id);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if (se.getAction().equals(Model.ChangeEvent.ChangeAction.DELETE)) {\n\n\t\t\t\t\t\tif (se.getUrl() != null) {\n\t\t\t\t\t\t\tif (se.getUrl().contains(\"tasks#task\")) {\n\t\t\t\t\t\t\t\tString id = EntityGCalAdapter.getIdFromTaskJSON(se.getUrl());\n\t\t\t\t\t\t\t\tif (id != null)\n\t\t\t\t\t\t\t\t\tremoveTask(id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tSyncLog.getReference().delete(se.getId(), se.getObjectType());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogEntry(LogEntry.ERROR, \" ERROR for: \" + se + \":\" + e.getMessage());\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\n\t\t}\n\t}\n\n\tprivate void updateTask(Task t) throws IOException {\n\t\ttry {\n\t\t\tlogEntry(LogEntry.UPDATE,\"Update Google Task:\" + t.toPrettyString());\n\t\t\ttservice.tasks().update(taskList, t.getId(), t).execute();\n\t\t} catch (Exception e) {\n\t\t\tlogEntry(LogEntry.WARN,\"WARNING: google doesn't know about task: \" + t.getId() + \" \" + t.getTitle() + \" try to add...\");\n\t\t\taddTask(t);\n\t\t}\n\t}\n\n\tprivate void removeTask(String id) throws IOException {\n\t\tlog.fine(\"removeTask:\" + id);\n\t\ttry {\n\t\t\tlogEntry(LogEntry.UPDATE,\"Delete Google Task:\" + id);\n\t\t\ttservice.tasks().delete(taskList, id).execute();\n\t\t} catch (IOException e) {\n\t\t\tif (e instanceof GoogleJsonResponseException ge) {\n\t\t\t\tif (ge.getDetails() != null && ge.getDetails().getCode() == 404) {\n\t\t\t\t\t// could not find task to delete in google - may have removed due date and then\n\t\t\t\t\t// deleted later\n\t\t\t\t\tlogEntry(\"Task not found in google - ignoring:\\n\" + e.getMessage());\n\t\t\t\t} else\n\t\t\t\t\tthrow e;\n\n\t\t\t} else\n\t\t\t\tthrow e;\n\t\t}\n\t}\n\n\tprivate void addTask(Task t) throws IOException {\n\t\tlogEntry(LogEntry.UPDATE,\"Add Google Task:\" + t.toPrettyString());\n\t\ttservice.tasks().insert(taskList, t).execute();\n\t}\n\n\tpublic Event getEvent(String id) {\n\t\ttry {\n\t\t\treturn service.events().get(calendarId, id).execute();\n\t\t} catch (IOException e) {\n\t\t\tlog.info(e.getMessage());\n\t\t\t// e.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void removeEvent(String id) throws IOException {\n\t\tlogEntry(LogEntry.UPDATE,\"Remove Google Event:\" + id);\n\t\tservice.events().delete(calendarId, id).execute();\n\t}\n\n\tpublic void updateEvent(Event ve1) throws IOException {\n\t\tlogEntry(LogEntry.UPDATE,\"Update Google Event:\" + ve1.toPrettyString());\n\t\tservice.events().update(calendarId, ve1.getId(), ve1).execute();\n\t}\n\n\tpublic void addEvent(Event ve1) throws IOException {\n\t\tve1.setId(null);\n\t\tlog.info(ve1.toPrettyString());\n\t\tlogEntry(LogEntry.UPDATE,\"Add Google Event:\" + ve1.toPrettyString());\n\t\tservice.events().insert(calendarId, ve1).execute();\n\t}\n\n\tprivate void syncFromServer(Date after, boolean cleanup) throws Exception {\n\n\t\tlogEntry(\" Start Incoming Sync\");\n\n\t\tString pageToken = \"\";\n\t\tint count = 0;\n\t\tArrayList<String> serverUids = new ArrayList<String>();\n\n\t\tDateTime a = new DateTime(after);\n\n\t\twhile (true) {\n\t\t\tEvents events = service.events().list(calendarId).setMaxResults(1000).setPageToken(pageToken)\n\t\t\t\t\t.setSingleEvents(false).execute();\n\t\t\tList<Event> items = events.getItems();\n\n\t\t\tlog.info(\" \" + a);\n\n\t\t\tlogEntry(\" found \" + items.size() + \" Event Calendars on server\");\n\n\t\t\tfor (Event event : items) {\n\t\t\t\tcount += syncEvent(event, serverUids);\n\t\t\t}\n\n\t\t\tif (events.getNextPageToken() == null)\n\t\t\t\tbreak;\n\n\t\t\tpageToken = events.getNextPageToken();\n\n\t\t}\n\n\t\tlogEntry(\" processed \" + count + \" new/changed Events\");\n\n\t\tcount = 0;\n\t\tpageToken = \"\";\n\t\twhile (true) {\n\t\t\tcom.google.api.services.tasks.model.Tasks result2 = tservice.tasks().list(taskList).setMaxResults(100)\n\t\t\t\t\t.setPageToken(pageToken).execute();\n\t\t\tList<Task> tasks = result2.getItems();\n\t\t\tif (tasks != null) {\n\t\t\t\tlogEntry(\" found \" + tasks.size() + \" Tasks on server \");\n\n\t\t\t\tfor (Task task : tasks) {\n\t\t\t\t\tcount += syncTask(task, serverUids);\n\t\t\t\t}\n\t\t\t} else\n\t\t\t\tbreak;\n\n\t\t\tif (result2.getNextPageToken() == null)\n\t\t\t\tbreak;\n\n\t\t\tpageToken = result2.getNextPageToken();\n\t\t}\n\t\tlogEntry(\" processed \" + count + \" new/changed Tasks\");\n\n\t\tlog.fine(serverUids.toString());\n\n\t\tlogEntry(\" check for deletes\");\n\n\t\t// find all appts in Borg that are not on the server\n\t\tfor (Appointment ap : AppointmentModel.getReference().getAllAppts()) {\n\t\t\tif (ap.getDate().before(after))\n\t\t\t\tcontinue;\n\n\t\t\t// if appt was not synced, then don't delete\n\t\t\tif (ap.getUrl() == null || !ap.getUrl().contains(\"etag\")) {\n\t\t\t\tlogEntry(LogEntry.WARN,\"*** Appointment is not synced with google??? \\n\" + ap);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// NOTE - a delete of a google task will not cause delete of the BORG appt\n\t\t\tif (!serverUids.contains(ap.getUid())) {\n\n\t\t\t\tif (ap.isTodo() && !cleanup) {\n\t\t\t\t\tlogEntry(LogEntry.WARN,\"*** Todo not found on google - WILL LEAVE IN BORG: \\n\" + ap + \" \\n*** Use Sync with cleanup option to delete or handle manually\");\n\n\t\t\t\t} else {\n\t\t\t\t\tlogEntry(LogEntry.UPDATE,\"Appointment Not Found on server - Deleting: \" + ap);\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\t\tAppointmentModel.getReference().delAppt(ap.getKey());\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate int syncEvent(Event event, ArrayList<String> serverUids) throws Exception {\n\n\t\tlog.fine(\"Incoming event: \" + event.toString());\n\n\t\tString uid = event.getICalUID();\n\n\t\t// ignore cancelled events - gcal seems to keep a placeholder for cancelled\n\t\t// instances in a repeating series\n\t\tif (uid == null && event.getStatus().equalsIgnoreCase(\"cancelled\")) {\n\t\t\tlog.info(\"Ignoring cancelled event: \" + event.toString());\n\t\t\treturn 0;\n\t\t}\n\n\t\t// ignore incoming tasks\n\t\t// TODO - process completion??\n\t\tif (uid.contains(\"BORGT\") || uid.contains(\"BORGS\"))\n\t\t\treturn 0;\n\n\t\tserverUids.add(uid);\n\n\t\t// detect single occurrence\n\t\tboolean recur = false;\n\t\tif (event.getRecurrence() != null && !event.getRecurrence().isEmpty()) {\n\t\t\tfor (String rule : event.getRecurrence()) {\n\t\t\t\tif (rule.startsWith(\"RRULE:\")) {\n\t\t\t\t\trecur = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlog.fine(\"Incoming event: \" + event);\n\n\t\tAppointment newap = EntityGCalAdapter.toBorg(event);\n\t\tif (newap == null)\n\t\t\treturn 0;\n\n\t\tAppointment ap = AppointmentModel.getReference().getApptByUid(uid);\n\t\tif (ap == null) {\n\t\t\t// not found in BORG, so add it\n\t\t\ttry {\n\n\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\tlog.info(\" save: \" + event);\n\t\t\t\tlogEntry(LogEntry.UPDATE,\" save borg appt: \" + newap);\n\t\t\t\tAppointmentModel.getReference().saveAppt(newap);\n\t\t\t} finally {\n\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t}\n\t\t\treturn 1;\n\t\t} else {\n\n\t\t\t// if Borg has same etag as google, then skip\n\t\t\tif (ap.getUrl() != null) {\n\t\t\t\tEvent orig = new GsonFactory().fromString(ap.getUrl(), Event.class);\n\t\t\t\tif (orig.getEtag().equals(event.getEtag())) {\n\t\t\t\t\tlog.fine(\"Etags match - skipping \" + event.getSummary());\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ******* for recurring events, only update the text (and url) - so don't\n\t\t\t// update more than that on the google side\n\t\t\t// to make more changes than that - delete and add on google\n\t\t\tif (recur) {\n\t\t\t\tlogEntry(LogEntry.WARN,\"*** recurring event, partial update only: \" + event);\n\t\t\t\ttry {\n\t\t\t\t\tap.setText(newap.getText());\n\t\t\t\t\tap.setUrl(newap.getUrl());\n\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\t\tlog.info(\" save: \" + event);\n\t\t\t\t\tlogEntry(LogEntry.UPDATE,\" save borg appt: \" + ap);\n\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t} finally {\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnewap.setKey(ap.getKey());\n\t\t\t\tnewap.setReminderTimes(ap.getReminderTimes());\n\t\t\t\tnewap.setEncrypted(ap.isEncrypted());\n\n\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\tlog.info(\" save: \" + event);\n\t\t\t\tlogEntry(LogEntry.UPDATE,\" save borg appt: \" + newap);\n\t\t\t\tAppointmentModel.getReference().saveAppt(newap);\n\t\t\t} finally {\n\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t}\n\t\t\treturn 1;\n\n\t\t}\n\n\t}\n\n\tprivate int syncTask(Task task, ArrayList<String> serverUids) throws Exception {\n\n\t\t// limited task sync - google tasks are limited in functionality\n\t\t// BORG is the master. edits on the google side are not fully supported\n\t\t// Here are the limited set of sync conditions when updates are made to google\n\t\t// tasks:\n\t\t// 1. complete task on google -> do_todo on BORG\n\t\t// 2. move non-recurring tasks on google -> update date only in BORG\n\t\t// 3. date changed forward on recurring tasks - do_todo in BORG\n\t\t// this case assumes the task was completed using aCalendar+ on ANDROID, which\n\t\t// handles recurrence, and not a date change to the task\n\t\t// BORG recurring tasks are NOT recurring on the google side, except in\n\t\t// aCalendar+, so we are limited in what we should do when they\n\t\t// are updated on the google side\n\t\t// 4. brand new google task - save as new\n\t\t// Otherwise, no change in BORG\n\n\t\tlog.fine(\"Incoming task: \" + task.toPrettyString());\n\n\t\tint idx = -1;\n\t\tString notes = task.getNotes();\n\t\tif (notes != null)\n\t\t\tidx = notes.indexOf(\"UID:\");\n\t\tif (idx != -1) {\n\t\t\t// match to BORG appt\n\t\t\tString uid = notes.substring(idx + 4);\n\t\t\tif (uid.contains(\"BORGT\")) {\n\t\t\t\t// this is where a newly created task gets the URL updated\n\t\t\t\tnet.sf.borg.model.entity.Task bt = TaskModel.getReference().getTaskByUid(uid);\n\t\t\t\tif (bt != null && bt.getUrl() == null) {\n\t\t\t\t\tbt.setUrl(task.toPrettyString());\n\t\t\t\t\ttry {\n\t\t\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\t\t\tlogEntry(LogEntry.UPDATE,\" save: \" + bt);\n\t\t\t\t\t\tTaskModel.getReference().savetask(bt);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t\t\t}\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t} else if (uid.contains(\"BORGS\")) {\n\t\t\t\t// this is where a newly created task gets the URL updated\n\t\t\t\tnet.sf.borg.model.entity.Subtask bt = TaskModel.getReference().getSubTaskByUid(uid);\n\t\t\t\tif (bt != null && bt.getUrl() == null) {\n\t\t\t\t\tbt.setUrl(task.toPrettyString());\n\t\t\t\t\ttry {\n\t\t\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\t\t\tlogEntry(LogEntry.UPDATE,\" save: \" + bt);\n\t\t\t\t\t\tTaskModel.getReference().saveSubTask(bt);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t\t\t}\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tserverUids.add(uid);\n\t\t\tAppointment ap = AppointmentModel.getReference().getApptByUid(uid);\n\t\t\tif (ap == null) {\n\t\t\t\tlogEntry(LogEntry.WARN,\" ***WARNING*** could not find appt with UID: \" + uid + \" ignoring google task ....\");\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t// this is where a newly created todo gets the URL updated\n\t\t\tif (ap.getUrl() == null) {\n\t\t\t\tap.setUrl(task.toPrettyString());\n\t\t\t\ttry {\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(false);\n\t\t\t\t\tlog.info(\" save: \" + ap);\n\t\t\t\t\tlogEntry(\" Borg Update URL: \" + ap.getText());\n\n\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t} finally {\n\t\t\t\t\tSyncLog.getReference().setProcessUpdates(true);\n\t\t\t\t\t// keep going - if there was a problem and URL was incorrectly null, then\n\t\t\t\t\t// we still might have to do todo\n\t\t\t\t\t// return 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (task.getStatus().equals(\"completed\")) {\n\t\t\t\t// do_todo\n\t\t\t\tlogEntry(LogEntry.UPDATE,\" do_todo: \" + ap);\n\t\t\t\tAppointmentModel.getReference().do_todo(ap.getKey(), false);\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// TODO - check if date chgd - case where acalendar+ updates recurring todo\n\t\t\tDateTime due = new DateTime(task.getDue());\n\t\t\tDate taskDate = new Date(due.getValue() - tzOffset(due.getValue()));\n\t\t\tlog.fine(\"task due:\" + taskDate);\n\t\t\tlog.fine(\"ap due:\" + ap.getDate());\n\t\t\tlog.fine(\"ap nt:\" + ap.getNextTodo());\n\n\t\t\tDate d = ap.getNextTodo();\n\t\t\tif (d == null)\n\t\t\t\td = ap.getDate();\n\n\t\t\tlog.fine(\"ap doe:\" + DateUtil.dayOfEpoch(d));\n\t\t\tlog.fine(\"task doe:\" + DateUtil.dayOfEpoch(taskDate));\n\n\t\t\tif (Math.abs(taskDate.getTime() - d.getTime()) > 1000 * 60 * 60 * 12) {\n\t\t\t\tlogEntry(\"TODO time changed on google for \" + ap.getText());\n\n\t\t\t\t// if incoming date is greater than BORG date, then just do_todo\n\t\t\t\tif (ap.isRepeatFlag() && taskDate.getTime() - d.getTime() > 1000 * 60 * 60) { // 1 hr cushion - should\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// be exact though?\n\n\t\t\t\t\t// try to calculate next todo, assuming aCalendar+ was used - which advances the\n\t\t\t\t\t// due date\n\t\t\t\t\t// loop through future todo times and see if we find a match\n\t\t\t\t\tfor (Date nextTodo = AppointmentModel.getReference().next_todo(ap,\n\t\t\t\t\t\t\tnull); nextTodo != null; nextTodo = AppointmentModel.getReference().next_todo(ap,\n\t\t\t\t\t\t\t\t\tnextTodo)) {\n\n\t\t\t\t\t\tif (DateUtil.dayOfEpoch(nextTodo) == DateUtil.dayOfEpoch(taskDate)) {\n\t\t\t\t\t\t\t// set next todo to incoming task's due date\n\t\t\t\t\t\t\tap.setNextTodo(nextTodo);\n\t\t\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// for whatever reason, the google task's due date is not a value next todo\n\t\t\t\t\t// date, so just advance the todo\n\t\t\t\t\t// one time and alert the user to check\n\t\t\t\t\tlogEntry(LogEntry.WARN,\"CHECK: time advanced for repeating todo - do_todo - please verify\");\n\t\t\t\t\tAppointmentModel.getReference().do_todo(ap.getKey(), false);\n\t\t\t\t\treturn 1;\n\t\t\t\t} else if (!ap.isRepeatFlag()) {\n\t\t\t\t\tap.setDate(DateUtil.setToMidnight(taskDate));\n\t\t\t\t\tap.setText(task.getTitle());\n\t\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\t\tlogEntry(LogEntry.WARN,\"CHECK: non-repeating todo date change - please check:\" + ap);\n\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t} else if (!ap.getTitle().equals(task.getTitle())) {\n\t\t\t\t// text only chg\n\t\t\t\tap.setText(task.getTitle());\n\t\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\tlogEntry(LogEntry.WARN,\"CHECK: todo text-only change - please check:\\n\" + ap);\n\t\t\t}\n\n\t\t} else {\n\t\t\t// google created task - add new appt\n\t\t\tAppointment ap = EntityGCalAdapter.toBorg(task);\n\t\t\tif (ap == null) {\n\t\t\t\tlogEntry(LogEntry.WARN,\"Could not convert task: \" + task);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\tserverUids.add(ap.getUid());\n\t\t\tlogEntry(LogEntry.UPDATE,\" save from google-created task: \" + ap);\n\t\t\treturn 1;\n\n\t\t}\n\n\t\treturn 0;\n\n\t}\n\n\tpublic com.google.api.services.calendar.model.Calendar getCalendar(String id) throws IOException {\n\t\treturn service.calendars().get(id).execute();\n\t}\n\n\tpublic CalendarListEntry getCalendarListEntry(String id) throws IOException {\n\t\treturn service.calendarList().get(id).execute();\n\t}\n\n\t\n\tpublic void syncSubscribed(Date after) throws Exception {\n\n\t\tSubscribedCalendars.getReference().removeCals();\n\t\tfor (String id : subscribed) {\n\n\t\t\tlogEntry(\" Start Incoming Sync of Subscribed Calendar: \" + id);\n\n\t\t\tString pageToken = \"\";\n\n\t\t\tDateTime a = new DateTime(after);\n\n\t\t\twhile (true) {\n\t\t\t\tEvents events = service.events().list(id).setMaxResults(1000).setPageToken(pageToken)\n\t\t\t\t\t\t.setSingleEvents(false).execute();\n\t\t\t\tList<Event> items = events.getItems();\n\n\t\t\t\tlog.info(\" \" + a);\n\n\t\t\t\tlogEntry(\" found \" + items.size() + \" Event Calendars on server\");\n\n\t\t\t\tfor (Event event : items) {\n\n\t\t\t\t\tAppointment newap = EntityGCalAdapter.toBorg(event);\n\t\t\t\t\tif (newap == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tSubscribedCalendars.getReference().addEvent(newap, id);\n\n\t\t\t\t}\n\n\t\t\t\tif (events.getNextPageToken() == null)\n\t\t\t\t\tbreak;\n\n\t\t\t\tpageToken = events.getNextPageToken();\n\n\t\t\t}\n\t\t}\n\n\t\tSubscribedCalendars.getReference().createCache();\n\t\tSubscribedCalendars.getReference().refresh();\n\t}\n\n\t/**\n\t * sync all todos to a separate calendar as a crude way to share todos with\n\t * another google user. Todos in google are currently not shareable.\n\t * \n\t * @throws Exception\n\t */\n\tprivate void syncTodoCalendar() throws Exception {\n\n\t\tif (todoCalId == null)\n\t\t\treturn;\n\n\t\tList<Event> todoEvents = new ArrayList<Event>();\n\n\t\t// read all todos on the server\n\t\tlogEntry(\" Read all Events from Todo Calendar\");\n\n\t\tString pageToken = \"\";\n\n\t\twhile (true) {\n\t\t\tEvents events = service.events().list(todoCalId).setMaxResults(1000).setPageToken(pageToken)\n\t\t\t\t\t.setSingleEvents(false).execute();\n\t\t\ttodoEvents.addAll(events.getItems());\n\n\t\t\tif (events.getNextPageToken() == null)\n\t\t\t\tbreak;\n\n\t\t\tpageToken = events.getNextPageToken();\n\n\t\t}\n\n\t\tlogEntry(\" found \" + todoEvents.size() + \" Todo Events on server\");\n\n\t\t// compare with todos in BORG and add/delete/update in google as needed\n\t\tCollection<CalendarEntity> borgTodos = new ArrayList<CalendarEntity>();\n\t\tborgTodos.addAll(AppointmentModel.getReference().get_todos());\n\n\t\tCollection<net.sf.borg.model.entity.Task> tasks = TaskModel.getReference().getTasks();\n\t\tfor (net.sf.borg.model.entity.Task task : tasks) {\n\t\t\tif (TaskModel.isClosed(task) || task.getDate() == null)\n\t\t\t\tcontinue;\n\t\t\tborgTodos.add(task);\n\t\t}\n\t\tCollection<Subtask> subtasks = TaskModel.getReference().getSubTasks();\n\t\tfor (Subtask subtask : subtasks) {\n\t\t\tif (subtask.getDueDate() == null || subtask.getCloseDate() != null)\n\t\t\t\tcontinue;\n\t\t\tborgTodos.add(subtask);\n\t\t}\n\n\t\tfor (CalendarEntity borgTodo : borgTodos) {\n\n\t\t\tif (borgTodo.isPrivate())\n\t\t\t\tcontinue;\n\n\t\t\tDate nt = borgTodo.getNextTodo();\n\t\t\tif (nt == null) {\n\t\t\t\tnt = borgTodo.getDate();\n\t\t\t}\n\n\t\t\tif (nt == null)\n\t\t\t\tcontinue;\n\n\t\t\tboolean found_on_server = false;\n\n\t\t\tfor (Event ge : todoEvents) {\n\n\t\t\t\t// match by text and date only\n\t\t\t\tif (borgTodo.getTitle().equals(ge.getSummary())) {\n\n\t\t\t\t\tDate utc = new Date();\n\t\t\t\t\tutc.setTime(ge.getStart().getDate().getValue() - tzOffset(ge.getStart().getDate().getValue()));\n\n\t\t\t\t\tif (nt.equals(utc)) {\n\t\t\t\t\t\t// match found\n\t\t\t\t\t\ttodoEvents.remove(ge);\n\t\t\t\t\t\tfound_on_server = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (!found_on_server) {\n\n\t\t\t\t// add to server\n\t\t\t\tEvent ve1 = EntityGCalAdapter.toGCalDummyEvent(borgTodo);\n\t\t\t\tve1.setId(null);\n\t\t\t\tlog.info(ve1.toPrettyString());\n\t\t\t\tservice.events().insert(todoCalId, ve1).execute();\n\t\t\t}\n\n\t\t}\n\n\t\t// delete leftover google Events\n\t\tfor (Event ge : todoEvents) {\n\t\t\tservice.events().delete(todoCalId, ge.getId()).execute();\n\t\t}\n\n\t}\n\n\tprivate void logEntry(String text) {\n\t\tlogEntry(LogEntry.INFO,text);\n\t}\n\tprivate void logEntry(String type, String text) {\n\t\tlogEntries.add(new LogEntry(type, text));\n\t\tif (type.equals(LogEntry.ERROR)) {\n\t\t\tlog.severe(text);\n\t\t} else if (type.equals(LogEntry.WARN)) {\n\t\t\tlog.warning(text);\n\t\t} else {\n\t\t\tlog.info(text);\n\t\t}\n\t\tModalMessageServer.getReference().sendLogMessage(text);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/google/GDrive.java",
    "content": "package net.sf.borg.model.sync.google;\n\nimport java.awt.BorderLayout;\nimport java.awt.FlowLayout;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.SwingWorker;\nimport javax.swing.UIManager;\n\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;\nimport com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.store.FileDataStoreFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.gson.stream.MalformedJsonException;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.DbDirtyManager;\nimport net.sf.borg.model.db.jdbc.JdbcDB;\n\n/*\n * Much of this code brought to you by google gemini\n */\n\npublic class GDrive {\n\tprivate static final String APPLICATION_NAME = \"BORG Calendar\";\n\tprivate static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\tprivate static final List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE);\n\n\tprivate Drive service = null;\n\tstatic volatile private GDrive singleton = null;\n\n\tstatic public GDrive getReference() {\n\t\tif (singleton == null) {\n\t\t\tGDrive b = new GDrive();\n\t\t\tsingleton = b;\n\t\t}\n\t\treturn (singleton);\n\t}\n\n\tpublic void connect() throws Exception {\n\n\t\tString googleFileId = Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH);\n\t\tif (googleFileId == null || googleFileId.isEmpty()) {\n\t\t\tlog.info(\"GDrive:connect(): Google File Id is not set\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (service != null)\n\t\t\treturn;\n\n\t\t// Build a new authorized API client service.\n\t\tfinal NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n\t\tservice = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n\t\t\t\t.setApplicationName(APPLICATION_NAME).build();\n\n\t}\n\n\tprivate Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws Exception {\n\t\t// Load client secrets.\n\t\tjava.io.File f = new java.io.File(Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\n\t\ttry {\n\t\t\tInputStream in = new FileInputStream(f);\n\n\t\t\tGoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));\n\n\t\t\t// Build flow and trigger user authorization request.\n\t\t\tGoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,\n\t\t\t\t\tclientSecrets, SCOPES)\n\t\t\t\t\t.setDataStoreFactory(new FileDataStoreFactory(\n\t\t\t\t\t\t\tnew java.io.File(Prefs.getPref(PrefName.GOOGLE_TOKEN_DIR) + \"/drivecred\")))\n\t\t\t\t\t.setAccessType(\"offline\").build();\n\t\t\tLocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();\n\t\t\tCredential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize(\"user\");\n\t\t\t// returns an authorized Credential object.\n\t\t\treturn credential;\n\t\t} catch (FileNotFoundException fe) {\n\t\t\tthrow new Exception(\"Credentials File not found: \" + Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\t\t} catch (MalformedJsonException mj) {\n\t\t\tlog.severe(mj.toString());\n\t\t\tthrow new Exception(\"could not parse JSON credentials file: \" + Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n\t\t}\n\n\t}\n\n\tpublic void checkModTimes() throws IOException {\n\n\t\tString googleFilePath = Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH);\n\t\tif (googleFilePath == null || googleFilePath.isEmpty()) {\n\t\t\tlog.info(\"GDrive:checkModTimes(): Google File Id is not set\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconnect();\n\t\t} catch (Exception e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\tshowErrorDialog(false, e.getMessage());\n\t\t\treturn;\n\t\t}\n\n\t\tDriveFileManager fileManager = new DriveFileManager(service);\n\n\t\tString googleFileId = null;\n\t\ttry {\n\t\t\tString fileMimeType = \"application/octet-stream\";\n\n\t\t\tgoogleFileId = fileManager.findOrCreateFile(googleFilePath, fileMimeType);\n\n\t\t\tlog.info(\"Final File ID: \" + googleFileId);\n\n\t\t} catch (IOException e) {\n\t\t\tSystem.err.println(\"An error occurred: \" + e.getMessage());\n\t\t\te.printStackTrace();\n\t\t\tshowErrorDialog(false, \"<html>\" + e.getMessage() + \":\" + e.getClass() + \"<br/>Accessing the google file failed, Error: \"\n\t\t\t\t\t+ e.getMessage() + \"<br/>It is recommended that you exit and fix the issue manually</html>\");\n\t\t\treturn;\n\t\t}\n\n\t\tString localDBFile = JdbcDB.getDBFilePath();\n\t\tif (localDBFile == null)\n\t\t\treturn;\n\n\t\tFile fileMeta = service.files().get(googleFileId).setFields(\"id,name,modifiedTime\").execute();\n\n\t\tlog.info(\"Google File Mod: \" + fileMeta.getName() + \" \" + fileMeta.getModifiedTime() + \" \"\n\t\t\t\t+ fileMeta.getModifiedTime().getValue());\n\n\t\tjava.io.File f = new java.io.File(localDBFile);\n\n\t\tif (f.exists()) {\n\n\t\t\tlong lastModifiedMillis = f.lastModified();\n\t\t\tDate lastModifiedDate = new Date(lastModifiedMillis);\n\t\t\tDate gdate = new Date(fileMeta.getModifiedTime().getValue());\n\n\t\t\tlog.info(\"Database File Mod: \" + localDBFile + \" \" + lastModifiedMillis + \" \" + lastModifiedDate);\n\n\t\t\tif (lastModifiedMillis + 1000 * 60 < fileMeta.getModifiedTime().getValue()) {\n\t\t\t\tString msg = \"<html>Google DB file is newer than local File, sync may be needed<br/>\" + \"Google file: \"\n\t\t\t\t\t\t+ fileMeta.getName() + \" \" + gdate + \"<br/>Local file: \" + localDBFile + \" \" + lastModifiedDate\n\t\t\t\t\t\t+ \"<br/>\" + \"Difference: \" + (fileMeta.getModifiedTime().getValue() - lastModifiedMillis) / 1000\n\t\t\t\t\t\t+ \" seconds</html>\";\n\t\t\t\tlog.info(msg);\n\t\t\t\tshowSyncNeededDialog(googleFileId, localDBFile, msg);\n\t\t\t\tPrefs.putPref(PrefName.GOOGLE_DB_FORCE_DOWNLOAD, \"false\");\n\n\n\t\t\t}else if( Prefs.getBoolPref(PrefName.GOOGLE_DB_FORCE_DOWNLOAD)) {\n\t\t\t\tString msg = \"<html>You requested to download the google DB file<br/>\" + \"Google file: \"\n\t\t\t\t\t\t+ fileMeta.getName() + \" \" + gdate + \"<br/>Local file: \" + localDBFile + \" \" + lastModifiedDate\n\t\t\t\t\t\t+ \"</html>\";\n\t\t\t\tlog.info(msg);\n\t\t\t\tshowSyncNeededDialog(googleFileId, localDBFile, msg);\n\t\t\t\tPrefs.putPref(PrefName.GOOGLE_DB_FORCE_DOWNLOAD, \"false\");\n\t\t\t}\n\n\t\t} else {\n\t\t\tlog.warning(localDBFile + \"Not Found\");\n\t\t}\n\t\t\n\t\t\n\n\t}\n\n\tprivate void downloadFile(String googleFileId, String localPath) {\n\n\t\tFileDownloader downloader = new FileDownloader();\n\n\t\ttry {\n\n\t\t\t// 2. Specify the local file path where you want to save the content\n\t\t\tPath downloadPath = Paths.get(localPath);\n\n\t\t\t// 3. Execute the download\n\t\t\tdownloader.downloadFile(service, googleFileId, downloadPath);\n\n\t\t} catch (IOException e) {\n\t\t\tSystem.err.println(\"Download failed: \" + e.getMessage());\n\t\t\tshowErrorDialog(false, \"<html>The download failed. Error: \" + e.getMessage()\n\t\t\t\t\t+ \"<br/>It is recommended that you exit and fix the issue manually</html>\");\n\t\t}\n\t}\n\n\tprivate void showSyncNeededDialog(String googleFileId, String localPath, String msg) {\n\n\t\t/*\n\t\t * gemini wrote 99% of the following, including the comments\n\t\t */\n\n\t\t// Create the main frame (or just use null for parent if not needed)\n\t\t// We'll use a hidden JFrame as the owner to properly center the dialog.\n\t\tJFrame ownerFrame = new JFrame();\n\t\townerFrame.setSize(0, 0); // Keep it invisible\n\t\townerFrame.setVisible(false);\n\n\t\tfinal JDialog dialog = new JDialog(ownerFrame, \"DB Startup\", true);\n\t\tdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\n\n\t\t// 2. Create the main content panel\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(20, 20)); // Padding between components\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20)); // Overall padding\n\n\t\t// --- Text Area ---\n\t\tJLabel textLabel = new JLabel(msg);\n\t\t// textLabel.setFont(new Font(\"SansSerif\", Font.PLAIN, 12));\n\n\t\t// Use a wrapper panel for the text to mimic a JOptionPane-style layout\n\t\t// (optional)\n\t\tJPanel textPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));\n\t\ttextPanel.add(textLabel);\n\t\tcontentPanel.add(textPanel, BorderLayout.CENTER);\n\n\t\t// --- Button Panel ---\n\t\tJPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));\n\n\t\t// 3. Create \"Exit the program\" button\n\t\tIcon exitIcon = UIManager.getIcon(\"OptionPane.informationIcon\"); // Using a standard information icon\n\t\tif (exitIcon == null) {\n\t\t\tlog.info(\"Could not find UIManager information icon, falling back to a dummy icon.\");\n\t\t\texitIcon = new ImageIcon(); // Fallback\n\t\t}\n\t\tJButton exitButton = new JButton(\"Exit the program\", exitIcon);\n\t\texitButton.addActionListener(e -> {\n\n\t\t\tlog.info(\"Exiting the program.\");\n\t\t\tSystem.exit(0); // Terminate the application\n\t\t});\n\n\t\t// For standard icons, we can try using a built-in one like error/warning icon:\n\t\tIcon stopIcon = UIManager.getIcon(\"OptionPane.errorIcon\");\n\t\tif (stopIcon == null) {\n\t\t\t// Fallback if the UIManager doesn't provide a suitable icon (less common)\n\t\t\tlog.info(\"Could not find UIManager icon, falling back to a dummy icon.\");\n\t\t\tstopIcon = new ImageIcon(); // Empty icon\n\t\t}\n\n\t\tJButton proceedButton1 = new JButton(\"Proceed Anyway\", stopIcon);\n\t\t// Optional: Set the button to be the default action\n\t\tdialog.getRootPane().setDefaultButton(proceedButton1);\n\n\t\tproceedButton1.addActionListener(e -> {\n\t\t\t// Get the checkbox state\n\t\t\tlog.info(\"User chose 'Proceed Anyway'.\");\n\n\t\t\t// In a real app, you would save the 'shouldHide' state here.\n\t\t\tdialog.dispose(); // Close the dialog\n\t\t});\n\n\t\tJButton proceedButton2 = new JButton(\"Download DB and Proceed\", exitIcon);\n\n\t\tproceedButton2.addActionListener(e -> {\n\t\t\t// Get the checkbox state\n\t\t\tlog.info(\"User chose 'Download and Proceed'.\");\n\n\t\t\t// In a real app, you would save the 'shouldHide' state here.\n\t\t\tdialog.dispose(); // Close the dialog\n\t\t\tdownloadFile(googleFileId, localPath);\n\t\t});\n\n\t\t// 5. Add buttons to the button panel\n\t\tbuttonPanel.add(exitButton);\n\t\tbuttonPanel.add(proceedButton1);\n\t\tbuttonPanel.add(proceedButton2);\n\n\t\t// Add all main components to the dialog\n\t\tdialog.add(contentPanel, BorderLayout.CENTER);\n\t\tdialog.add(buttonPanel, BorderLayout.SOUTH);\n\n\t\t// 6. Configure and show the dialog\n\t\tdialog.pack(); // Size the dialog based on its contents\n\t\tdialog.setLocationRelativeTo(ownerFrame); // Center the dialog on the screen (relative to the invisible owner)\n\t\tdialog.setVisible(true);\n\n\t}\n\n\tpublic void showUploadDialog() {\n\n\t\tString googleFilePath = Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH);\n\t\tif (googleFilePath == null || googleFilePath.isEmpty()) {\n\t\t\tlog.info(\"GDrive:checkModTimes(): Google File Path is not set\");\n\t\t\treturn;\n\t\t}\n\n\t\tString localDBFile = JdbcDB.getDBFilePath();\n\t\tif (localDBFile == null)\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tconnect();\n\t\t} catch (Exception e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t\t// Create the main frame (or just use null for parent if not needed)\n\t\t// We'll use a hidden JFrame as the owner to properly center the dialog.\n\t\tJFrame ownerFrame = new JFrame();\n\t\townerFrame.setSize(0, 0); // Keep it invisible\n\t\townerFrame.setVisible(false);\n\n\t\tfinal JDialog dialog = new JDialog(ownerFrame, \"DB Upload\", true);\n\t\tdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\n\n\t\t// 2. Create the main content panel\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(20, 20)); // Padding between components\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20)); // Overall padding\n\n\t\t// --- Text Area ---\n\t\tJLabel textLabel = new JLabel(\"Should I upload the database to Google Drive?\");\n\n\t\tJPanel textPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));\n\t\ttextPanel.add(textLabel);\n\t\tcontentPanel.add(textPanel, BorderLayout.CENTER);\n\n\t\t// --- Button Panel ---\n\t\tJPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));\n\n\t\tIcon exitIcon = UIManager.getIcon(\"OptionPane.informationIcon\"); // Using a standard information icon\n\t\tif (exitIcon == null) {\n\t\t\tlog.info(\"Could not find UIManager information icon, falling back to a dummy icon.\");\n\t\t\texitIcon = new ImageIcon(); // Fallback\n\t\t}\n\t\tJButton exitButton = new JButton(\"Yes, Upload\", exitIcon);\n\t\texitButton.addActionListener(e -> {\n\t\t\ttry {\n\n\t\t\t\tJDialog loadingDialog = new JDialog(ownerFrame, \"Please Wait\", true);\n\t\t\t\tloadingDialog.add(new JLabel(\"Uploading... Please wait.\"), BorderLayout.CENTER);\n\t\t\t\tloadingDialog.setSize(250, 100);\n\t\t\t\tloadingDialog.setLocationRelativeTo(ownerFrame);\n\t\t\t\tloadingDialog.setModal(true);\n\n\t\t\t\t// 2. Create the SwingWorker\n\t\t\t\tSwingWorker<Void, Void> worker = new SwingWorker<>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected Void doInBackground() throws Exception {\n\t\t\t\t\t\t\n\t\t\t\t\t\tGDrive.getReference().uploadFile(googleFilePath, localDBFile);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void done() {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tget();\n\t\t\t\t\t\t\tDbDirtyManager.getReference().setClean();\n\t\t\t\t\t\t\tJOptionPane.showMessageDialog(ownerFrame, \"Upload Completed Successfully!\");\n\t\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\t\tshowErrorDialog(true, \"<html>The upload failed. Error: \" + e1.getMessage()\n\t\t\t\t\t\t\t\t\t+ \"<br/>It is recommended that you exit and try again, or fix the issue manually</html>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tloadingDialog.dispose();\n\n\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// 4. Start the worker and show the dialog\n\t\t\t\tworker.execute();\n\t\t\t\tloadingDialog.setVisible(true);\n\t\t\t} catch (Exception e1) {\n\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\te1.printStackTrace();\n\t\t\t\tshowErrorDialog(true, \"<html>The upload failed. Error: \" + e1.getMessage()\n\t\t\t\t\t\t+ \"<br/>It is recommended that you exit and try again, or fix the issue manually</html>\");\n\n\t\t\t}\n\t\t\tdialog.dispose();\n\n\t\t});\n\n\t\t// For standard icons, we can try using a built-in one like error/warning icon:\n\t\tIcon stopIcon = UIManager.getIcon(\"OptionPane.errorIcon\");\n\t\tif (stopIcon == null) {\n\t\t\t// Fallback if the UIManager doesn't provide a suitable icon (less common)\n\t\t\tlog.info(\"Could not find UIManager icon, falling back to a dummy icon.\");\n\t\t\tstopIcon = new ImageIcon(); // Empty icon\n\t\t}\n\n\t\tJButton proceedButton1 = new JButton(\"Exit without Uploading\", stopIcon);\n\t\t// Optional: Set the button to be the default action\n\t\tdialog.getRootPane().setDefaultButton(proceedButton1);\n\n\t\tproceedButton1.addActionListener(e -> {\n\t\t\t// Get the checkbox state\n\t\t\tlog.info(\"User chose 'exit wihtout uploading'.\");\n\n\t\t\t// In a real app, you would save the 'shouldHide' state here.\n\t\t\tdialog.dispose(); // Close the dialog\n\t\t});\n\n\t\t// 5. Add buttons to the button panel\n\t\tbuttonPanel.add(exitButton);\n\t\tbuttonPanel.add(proceedButton1);\n\n\t\t// Add all main components to the dialog\n\t\tdialog.add(contentPanel, BorderLayout.CENTER);\n\t\tdialog.add(buttonPanel, BorderLayout.SOUTH);\n\n\t\t// 6. Configure and show the dialog\n\t\tdialog.pack(); // Size the dialog based on its contents\n\t\tdialog.setLocationRelativeTo(ownerFrame); // Center the dialog on the screen (relative to the invisible owner)\n\t\tdialog.setVisible(true);\n\n\t}\n\n\tprivate void uploadFile(String googleFilePath, String localPath) throws Exception {\n\t\tlog.info(\"Uploading file\");\n\t\tjava.io.File f = new java.io.File(localPath);\n\n\t\tDriveFileManager fileManager = new DriveFileManager(service);\n\n\t\tString googleFileId = null;\n\t\tString fileMimeType = \"application/octet-stream\";\n\n\t\tgoogleFileId = fileManager.findOrCreateFile(googleFilePath, fileMimeType);\n\n\t\tlog.info(\"Final File ID: \" + googleFileId);\n\n\t\tupdateFileContent(service, googleFileId, f.toPath(), \"application/octet-stream\");\n\n\t}\n\n\t/**\n\t * Updates the content of an existing file on Google Drive.\n\t *\n\t * @param driveService  Authenticated Drive service object.\n\t * @param fileId        The ID of the file to update.\n\t * @param localFilePath The local path to the new file content.\n\t * @param mimeType      The MIME type of the file being uploaded (e.g.,\n\t *                      \"image/jpeg\").\n\t * @return The updated File resource with new metadata.\n\t * @throws IOException If the API call fails.\n\t */\n\tprivate static void updateFileContent(Drive driveService, String fileId, Path localFilePath, String mimeType)\n\t\t\tthrows IOException {\n\n\t\t// 1. Prepare the new content\n\t\tjava.io.File fileToUpload = localFilePath.toFile();\n\t\tFileContent mediaContent = new FileContent(mimeType, fileToUpload);\n\n\t\t// 2. Prepare the metadata (optional, but good practice)\n\t\t// You can set new metadata like a new name or description,\n\t\t// but leaving it empty will keep the existing metadata.\n\t\tFile fileMetadata = new File();\n\t\t// Example: Update the file's name (optional)\n\t\t// fileMetadata.setName(localFilePath.getFileName().toString());\n\n\t\ttry {\n\t\t\t// 3. Execute the update call\n\t\t\t// The .update(fileId, fileMetadata, mediaContent) call uploads\n\t\t\t// the new content and updates the metadata (if provided).\n\t\t\tFile updatedFile = driveService.files().update(fileId, fileMetadata, mediaContent)\n\t\t\t\t\t.setFields(\"id, name, mimeType, modifiedTime, size\") // Specify fields to return\n\t\t\t\t\t.execute();\n\n\t\t\tSystem.out.println(\"File ID: \" + updatedFile.getId());\n\t\t\tSystem.out.println(\"New File Name: \" + updatedFile.getName());\n\t\t\tSystem.out.println(\"New File Size: \" + updatedFile.getSize());\n\n\t\t\t// return updatedFile;\n\n\t\t} catch (IOException e) {\n\t\t\tSystem.err.println(\"An error occurred during file update: \" + e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tpublic static void showErrorDialog(boolean exitOnly, String msg) {\n\n\t\t// Create the main frame (or just use null for parent if not needed)\n\t\t// We'll use a hidden JFrame as the owner to properly center the dialog.\n\t\tJFrame ownerFrame = new JFrame();\n\t\townerFrame.setSize(0, 0); // Keep it invisible\n\t\townerFrame.setVisible(false);\n\n\t\tfinal JDialog dialog = new JDialog(ownerFrame, \"Error\", true);\n\t\tdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\n\n\t\t// 2. Create the main content panel\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(20, 20)); // Padding between components\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20)); // Overall padding\n\n\t\t// --- Text Area ---\n\t\tJLabel textLabel = new JLabel(msg);\n\n\t\tJPanel textPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));\n\t\ttextPanel.add(textLabel);\n\t\tcontentPanel.add(textPanel, BorderLayout.CENTER);\n\n\t\t// --- Button Panel ---\n\t\tJPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));\n\n\t\tIcon exitIcon = UIManager.getIcon(\"OptionPane.informationIcon\"); // Using a standard information icon\n\t\tif (exitIcon == null) {\n\t\t\tlog.info(\"Could not find UIManager information icon, falling back to a dummy icon.\");\n\t\t\texitIcon = new ImageIcon(); // Fallback\n\t\t}\n\t\tJButton exitButton = new JButton(\"Exit\", exitIcon);\n\t\texitButton.addActionListener(e -> {\n\t\t\tdialog.dispose();\n\t\t\tSystem.exit(0);\n\n\t\t});\n\n\t\tbuttonPanel.add(exitButton);\n\n\t\tif (!exitOnly) {\n\n\t\t\t// For standard icons, we can try using a built-in one like error/warning icon:\n\t\t\tIcon stopIcon = UIManager.getIcon(\"OptionPane.errorIcon\");\n\t\t\tif (stopIcon == null) {\n\t\t\t\t// Fallback if the UIManager doesn't provide a suitable icon (less common)\n\t\t\t\tlog.info(\"Could not find UIManager icon, falling back to a dummy icon.\");\n\t\t\t\tstopIcon = new ImageIcon(); // Empty icon\n\t\t\t}\n\n\t\t\tJButton proceedButton1 = new JButton(\"Proceed anyway\", stopIcon);\n\t\t\t// Optional: Set the button to be the default action\n\t\t\tdialog.getRootPane().setDefaultButton(proceedButton1);\n\n\t\t\tproceedButton1.addActionListener(e -> {\n\t\t\t\t// Get the checkbox state\n\t\t\t\tlog.info(\"user chose to proceed\");\n\n\t\t\t\t// In a real app, you would save the 'shouldHide' state here.\n\t\t\t\tdialog.dispose(); // Close the dialog\n\t\t\t});\n\n\t\t\t// 5. Add buttons to the button panel\n\t\t\tbuttonPanel.add(proceedButton1);\n\t\t}\n\n\t\t// Add all main components to the dialog\n\t\tdialog.add(contentPanel, BorderLayout.CENTER);\n\t\tdialog.add(buttonPanel, BorderLayout.SOUTH);\n\n\t\t// 6. Configure and show the dialog\n\t\tdialog.pack(); // Size the dialog based on its contents\n\t\tdialog.setLocationRelativeTo(ownerFrame); // Center the dialog on the screen (relative to the invisible owner)\n\t\tdialog.setVisible(true);\n\n\t}\n\t\n\tpublic boolean isUploading() {\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\n\t\tif( !\"sqlite\".equals(dbtype)) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\tString googleFilePath = Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH);\n\t\tif (googleFilePath == null || googleFilePath.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn true;\n\t\t\n\t}\n\t\n\t/*\n\t * upload the db to google drive while the db connection is open\n\t * implemented for sqlite only using the VACUUM command\n\t */\n\tpublic void vacuumAndUpload() {\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\n\t\tif( !\"sqlite\".equals(dbtype)) {\n\t\t\tErrmsg.getErrorHandler().notice(\"Only Sqlite databases can be uploaded while BORG is running\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tString googleFilePath = Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH);\n\t\tif (googleFilePath == null || googleFilePath.isEmpty()) {\n\t\t\tErrmsg.getErrorHandler().notice(\"Google File Path is not set\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tString dbfolder = Prefs.getPref(PrefName.SQLITEDIR);\n\t\tString newDBFilename = \"borg_backup.db\";\n\t\tString fullpath = dbfolder + \"/\" + newDBFilename;\n\t\t\n\t\ttry {\n\t\t\tFiles.delete(Paths.get(fullpath));\n\t\t} catch (IOException e) {\n\t\t\t\n\t\t}\n\n\t\t// vacuum the db\n\t\ttry {\n\t\t\t\n\n\t\t\tDBHelper.getController().beginTransaction();\n\t\t\tDBHelper.getController().execQuery(\"VACUUM INTO '\" + fullpath + \"'\");\n\t\t\tDBHelper.getController().commitTransaction();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tJFrame ownerFrame = new JFrame();\n\t\townerFrame.setSize(0, 0); // Keep it invisible\n\t\townerFrame.setVisible(false);\n\t\tJDialog loadingDialog = new JDialog(ownerFrame, \"Please Wait\", true);\n\t\tloadingDialog.add(new JLabel(\"Uploading... Please wait.\"), BorderLayout.CENTER);\n\t\tloadingDialog.setSize(250, 100);\n\t\tloadingDialog.setLocationRelativeTo(ownerFrame);\n\t\tloadingDialog.setModal(true);\n\t\t\n\t\ttry {\n\t\t\t\n\n\t\t\t// 2. Create the SwingWorker\n\t\t\tSwingWorker<Void, Void> worker = new SwingWorker<>() {\n\t\t\t\t@Override\n\t\t\t\tprotected Void doInBackground() throws Exception {\n\t\t\t\t\t\n\t\t\t\t\tGDrive.getReference().uploadFile(googleFilePath, fullpath);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected void done() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tget();\n\t\t\t\t\t\tDbDirtyManager.getReference().setClean();\n\t\t\t\t\t\tJOptionPane.showMessageDialog(ownerFrame, \"Upload Completed Successfully!\");\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tshowErrorDialog(true, \"<html>The upload failed. Error: \" + e1.getMessage()\n\t\t\t\t\t\t\t\t+ \"<br/>It is recommended that you exit and try again, or fix the issue manually</html>\");\n\t\t\t\t\t}\n\t\t\t\t\tloadingDialog.dispose();\n\n\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// 4. Start the worker and show the dialog\n\t\t\tworker.execute();\n\t\t\tloadingDialog.setVisible(true);\n\t\t} catch (Exception e1) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te1.printStackTrace();\n\t\t\tshowErrorDialog(true, \"<html>The upload failed. Error: \" + e1.getMessage()\n\t\t\t\t\t+ \"<br/>It is recommended that you exit and try again, or fix the issue manually</html>\");\n\n\t\t}\n\t\tloadingDialog.dispose();\n\t\townerFrame.dispose();\n\t\t\n\t}\n\n}"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/ical/AddressVcardAdapter.java",
    "content": "package net.sf.borg.model.sync.ical;\n\nimport net.fortuna.ical4j.model.Date;\nimport net.fortuna.ical4j.vcard.Parameter;\nimport net.fortuna.ical4j.vcard.Property;\nimport net.fortuna.ical4j.vcard.VCard;\nimport net.fortuna.ical4j.vcard.VCardBuilder;\nimport net.fortuna.ical4j.vcard.parameter.Type;\nimport net.fortuna.ical4j.vcard.property.*;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.model.entity.Address;\n\nimport java.io.StringReader;\nimport java.net.URI;\nimport java.util.logging.Logger;\n\npublic class AddressVcardAdapter {\n\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n    static public Address fromVcard(VCard vc) {\n\n        if (vc == null)\n            return null;\n\n        Address addr = new Address();\n        addr.setVcard(vc.toString());\n\n        net.fortuna.ical4j.vcard.property.N name = (net.fortuna.ical4j.vcard.property.N) vc.getProperty(Property.Id.N);\n        if (name != null) {\n            addr.setFirstName(name.getGivenName());\n            addr.setLastName(name.getFamilyName());\n        }\n\n        net.fortuna.ical4j.vcard.property.Impp im = (Impp) vc.getProperty(Property.Id.IMPP);\n        if (im != null)\n            addr.setScreenName(im.getValue());\n\n        net.fortuna.ical4j.vcard.property.Nickname nick = (Nickname) vc.getProperty(Property.Id.NICKNAME);\n        if (nick != null)\n            addr.setNickname(nick.getValue());\n\n        net.fortuna.ical4j.vcard.property.BDay bday = (BDay) vc.getProperty(Property.Id.BDAY);\n        if (bday != null)\n            addr.setBirthday(bday.getDate());\n\n        net.fortuna.ical4j.vcard.property.Org org = (Org) vc.getProperty(Property.Id.ORG);\n        if (org != null)\n            addr.setCompany(org.getValue());\n\n        net.fortuna.ical4j.vcard.property.Note note = (Note) vc.getProperty(Property.Id.NOTE);\n        if (note != null)\n            addr.setNotes(note.getValue());\n\n        net.fortuna.ical4j.vcard.property.Url url = (Url) vc.getProperty(Property.Id.URL);\n        if (url != null)\n            addr.setWebPage(url.getValue());\n\n        for (Property p2 : vc.getProperties(Property.Id.EMAIL)) {\n            addr.setEmail(p2.getValue());\n            if (p2.getParameter(Parameter.Id.PREF) != null)\n                break;\n        }\n\n        for (Property p2 : vc.getProperties(Property.Id.ADR)) {\n            net.fortuna.ical4j.vcard.property.Address vcaddr = (net.fortuna.ical4j.vcard.property.Address) p2;\n            for (Parameter pm : p2.getParameters(Parameter.Id.TYPE)) {\n                Type t = (Type) pm;\n                if (t.getValue().contains(\"HOME\")) {\n                    addr.setStreetAddress(vcaddr.getStreet());\n                    addr.setCity(vcaddr.getLocality());\n                    addr.setState(vcaddr.getRegion());\n                    addr.setZip(vcaddr.getPostcode());\n                    addr.setCountry(vcaddr.getCountry());\n                } else if (t.getValue().contains(\"WORK\")) {\n                    addr.setWorkStreetAddress(vcaddr.getStreet());\n                    addr.setWorkCity(vcaddr.getLocality());\n                    addr.setWorkState(vcaddr.getRegion());\n                    addr.setWorkZip(vcaddr.getPostcode());\n                    addr.setWorkCountry(vcaddr.getCountry());\n                }\n            }\n        }\n\n        for (Property p2 : vc.getProperties(Property.Id.TEL)) {\n            for (Parameter pm : p2.getParameters(Parameter.Id.TYPE)) {\n                Type t = (Type) pm;\n                if (t.getValue().contains(\"HOME\"))\n                    addr.setHomePhone(p2.getValue());\n                else if (t.getValue().contains(\"WORK\") && !t.getValue().contains(\"FAX\"))\n                    addr.setWorkPhone(p2.getValue());\n                else if (t.getValue().contains(\"FAX\"))\n                    addr.setFax(p2.getValue());\n                else if (t.getValue().contains(\"CELL\"))\n                    addr.setCellPhone(p2.getValue());\n                else if (t.getValue().contains(\"PAGER\"))\n                    addr.setPager(p2.getValue());\n            }\n        }\n\n        log.fine(addr.toString());\n        return addr;\n    }\n\n    static public VCard toVcard(Address addr) {\n\n        VCard vc = new VCard();\n\n        vc.getProperties().add(new N(addr.getLastName(), addr.getFirstName(), null, null, null));\n        if (addr.getScreenName() != null)\n            try {\n                vc.getProperties().add(new Impp(new URI(addr.getScreenName())));\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().notice(e.getMessage());\n            }\n\n        if (addr.getNickname() != null)\n            vc.getProperties().add(new Nickname(addr.getNickname()));\n\n        if (addr.getBirthday() != null)\n            vc.getProperties().add(new BDay(new Date(addr.getBirthday())));\n\n        if (addr.getCompany() != null)\n            vc.getProperties().add(new Org(addr.getCompany()));\n\n        if (addr.getNotes() != null)\n            vc.getProperties().add(new Note(addr.getNotes()));\n\n        if (addr.getWebPage() != null)\n            try {\n                vc.getProperties().add(new Url(new URI(addr.getWebPage())));\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().notice(e.getMessage());\n            }\n\n        if (addr.getEmail() != null)\n            vc.getProperties().add(new Email(addr.getEmail()));\n\n        if (addr.getStreetAddress() != null || addr.getCity() != null || addr.getState() != null || addr.getCountry() != null || addr.getZip() != null)\n            vc.getProperties().add(new net.fortuna.ical4j.vcard.property.Address(null, null, addr.getStreetAddress(), addr.getCity(), addr.getState(), addr.getZip(), addr.getCountry(), new Type(\"HOME\")));\n\n        if (addr.getWorkStreetAddress() != null || addr.getWorkCity() != null || addr.getWorkState() != null || addr.getWorkCountry() != null || addr.getWorkZip() != null)\n            vc.getProperties().add(new net.fortuna.ical4j.vcard.property.Address(null, null, addr.getWorkStreetAddress(), addr.getWorkCity(), addr.getWorkState(), addr.getWorkZip(), addr.getWorkCountry(), new Type(\"WORK\")));\n\n        if (addr.getCellPhone() != null)\n            vc.getProperties().add(new Telephone(addr.getCellPhone(), new Type(\"CELL\")));\n        if (addr.getHomePhone() != null)\n            vc.getProperties().add(new Telephone(addr.getHomePhone(), new Type(\"HOME\")));\n        if (addr.getWorkPhone() != null)\n            vc.getProperties().add(new Telephone(addr.getWorkPhone(), new Type(\"WORK\")));\n\n        if (addr.getVcard() != null && !addr.getVcard().isEmpty()) {\n\n            try (StringReader r = new StringReader(addr.getVcard())) {\n                VCardBuilder builder = new VCardBuilder(r);\n                VCard vc2 = builder.build();\n                if (vc2 != null) {\n                    for (Property p : vc2.getProperties()) {\n                        if (vc.getProperties(p.getId()).isEmpty()) {\n                            vc.getProperties().add(p);\n                        }\n\n                    }\n                }\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().errmsg(e);\n                return null;\n            }\n        }\n\n        log.fine(vc.toString());\n\n        return vc;\n    }\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/ical/CardDav.java",
    "content": "package net.sf.borg.model.sync.ical;\n\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport net.fortuna.ical4j.vcard.VCard;\nimport net.fortuna.ical4j.vcard.VCardBuilder;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.IOHelper;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.entity.Address;\n\npublic class CardDav {\n\t\n\t//static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tstatic private void setHints() {\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);\n\t}\n\t\n\t\n\tpublic static void exportToFile(String filename) throws Exception {\n\t\t\n\t\tOutputStream oostr = IOHelper.createOutputStream(filename);\n\t\tOutputStreamWriter wr = new OutputStreamWriter(oostr);\n\t\t\n\t\tfor( Address addr : AddressModel.getReference().getAddresses()) {\n\t\t\twr.write(AddressVcardAdapter.toVcard(addr).toString());\n\t\t}\n\n\t\twr.close();\n\t\toostr.close();\n\t}\n\t\n\tstatic public List<VCard> importVcardFromFile(String file) throws Exception {\n\t\t\n\t\tsetHints();\n\t\t\n\t\tInputStream is = new FileInputStream(file);\n\t\treturn importVcardFromInputStream(is);\n\t}\n\t\n\tstatic public List<VCard> importVcardFromInputStream(InputStream is) throws Exception {\n\n\t\tsetHints();\n\n\t\tVCardBuilder builder = new VCardBuilder(is);\n\t\tList<VCard> l = builder.buildAll();\n\t\tis.close();\n\n\t\treturn l;\n\t}\n\n\tstatic public String importVCard(List<VCard> vcards) throws Exception {\n\n\t\tsetHints();\n\n\t\tStringBuffer warning = new StringBuffer();\n\n\t\tArrayList<Address> addrs = new ArrayList<Address>();\n\n\t\tAddressModel amodel = AddressModel.getReference();\n\t\tfor( VCard vc : vcards){\n\n\t\t\tAddress addr = AddressVcardAdapter.fromVcard(vc);\n\t\t\tif (addr != null)\n\t\t\t\taddrs.add(addr);\n\n\t\t}\n\n\t\tint imported = 0;\n\n\t\tfor (Address addr : addrs) {\n\n\t\t\ttry {\n\t\t\t\tamodel.saveAddress(addr);\n\t\t\t\timported++;\n\t\t\t}\n\t\t\tcatch(Warning w){\n\t\t\t\tErrmsg.getErrorHandler().notice(w.getMessage() + \"\\n\\n\" + addr.toString());\n\t\t\t}\n\t\t\tcatch(Exception e){\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t}\n\n\t\tint dels = amodel.removeDuplicates();\n\n\t\twarning.append(\"Imported: \" + imported + \"\\n\");\n\t\twarning.append(\"Duplicates Removed: \" + dels + \"\\n\");\n\t\twarning.append(\"Skipped: \" + (vcards.size() - imported) + \"\\n\");\n\n\t\tif (warning.length() == 0)\n\t\t\treturn (null);\n\n\t\treturn (warning.toString());\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/ical/EntityIcalAdapter.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.sync.ical;\n\nimport java.net.URISyntaxException;\nimport java.text.ParseException;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.TimeZone;\nimport java.util.UUID;\nimport java.util.Vector;\nimport java.util.logging.Logger;\n\nimport net.fortuna.ical4j.model.Component;\nimport net.fortuna.ical4j.model.DateList;\nimport net.fortuna.ical4j.model.DateTime;\nimport net.fortuna.ical4j.model.Dur;\nimport net.fortuna.ical4j.model.ParameterList;\nimport net.fortuna.ical4j.model.Property;\nimport net.fortuna.ical4j.model.PropertyList;\nimport net.fortuna.ical4j.model.Recur;\nimport net.fortuna.ical4j.model.TextList;\nimport net.fortuna.ical4j.model.component.CalendarComponent;\nimport net.fortuna.ical4j.model.component.VAlarm;\nimport net.fortuna.ical4j.model.component.VEvent;\nimport net.fortuna.ical4j.model.component.VToDo;\nimport net.fortuna.ical4j.model.parameter.Value;\nimport net.fortuna.ical4j.model.property.Action;\nimport net.fortuna.ical4j.model.property.Categories;\nimport net.fortuna.ical4j.model.property.Clazz;\nimport net.fortuna.ical4j.model.property.Created;\nimport net.fortuna.ical4j.model.property.DateProperty;\nimport net.fortuna.ical4j.model.property.Description;\nimport net.fortuna.ical4j.model.property.DtEnd;\nimport net.fortuna.ical4j.model.property.DtStart;\nimport net.fortuna.ical4j.model.property.Due;\nimport net.fortuna.ical4j.model.property.Duration;\nimport net.fortuna.ical4j.model.property.ExDate;\nimport net.fortuna.ical4j.model.property.LastModified;\nimport net.fortuna.ical4j.model.property.RRule;\nimport net.fortuna.ical4j.model.property.Status;\nimport net.fortuna.ical4j.model.property.Summary;\nimport net.fortuna.ical4j.model.property.Uid;\nimport net.fortuna.ical4j.model.property.Url;\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.model.sync.RecurrenceRule;\n\npublic class EntityIcalAdapter {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tstatic public CalendarComponent toIcal(Appointment ap, boolean export_todos) throws Exception {\n\n\t\tTextList catlist = new TextList();\n\t\tCalendarComponent ve = null;\n\n\t\t// export todos as VTODOs if option set\n\t\t// This works well in clients such as Mozilla Lightning, which handles\n\t\t// VTODOs similar to BORG.\n\t\t// On the other hand, VTODOs are not handled well at all by Android\n\t\tif (ap.isTodo() && export_todos)\n\t\t\tve = new VToDo();\n\t\telse\n\t\t\tve = new VEvent();\n\n\t\tString uidval = ap.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = ap.getKey() + \"@BORGA-\" + ap.getCreateTime().getTime();\n\t\t}\n\t\tUid uid = new Uid(uidval);\n\t\tve.getProperties().add(uid);\n\n\t\ttry {\n\n\t\t\tString urlVal = ap.getUrl();\n\t\t\tif (urlVal != null && !urlVal.isEmpty()) {\n\t\t\t\tUrl url = new Url();\n\t\t\t\turl.setValue(urlVal);\n\t\t\t\tve.getProperties().add(url);\n\t\t\t}\n\t\t} catch (URISyntaxException e) {\n\n\t\t\t// ignore url parsing. it's almost certainly because url is filled with google\n\t\t\t// calendar data from google sync\n\t\t\tlog.fine(e.toString() + \" \" + ap.getUrl());\n\t\t}\n\n\t\tve.getProperties().add(new Created(new DateTime(ap.getCreateTime())));\n\t\tve.getProperties().add(new LastModified(new DateTime(ap.getLastMod())));\n\n\t\t// add text\n\t\tString appttext = ap.getText();\n\t\tSummary sum = null;\n\t\tDescription desc = null;\n\n\t\tint ii = appttext.indexOf('\\n');\n\t\tif (ii != -1) {\n\t\t\tsum = new Summary(appttext.substring(0, ii));\n\t\t\tdesc = new Description(appttext.substring(ii + 1));\n\t\t} else {\n\t\t\tsum = new Summary(appttext);\n\t\t}\n\n\t\tve.getProperties().add(sum);\n\t\tif (desc != null) {\n\t\t\tve.getProperties().add(desc);\n\t\t}\n\n\t\tParameterList pl = new ParameterList();\n\n\t\t// date\n\t\tif (ve instanceof VToDo) {\n\t\t\t// date is the next todo field if present, otherwise\n\t\t\t// the due date\n\t\t\tDate nt = ap.getNextTodo();\n\t\t\tif (nt == null) {\n\t\t\t\tnt = ap.getDate();\n\t\t\t}\n\n\t\t\tif (AppointmentModel.isNote(ap)) {\n\t\t\t\tpl.add(Value.DATE);\n\t\t\t\tDtStart dtd = new DtStart(pl, new net.fortuna.ical4j.model.Date(nt));\n\t\t\t\tve.getProperties().add(dtd);\n\n\t\t\t\tVToDo todo = (VToDo) ve;\n\t\t\t\tDue due = new Due(pl, new net.fortuna.ical4j.model.Date(nt));\n\t\t\t\ttodo.getProperties().add(due);\n\t\t\t} else {\n\n\t\t\t\t// set time from appt date - next todo has no time\n\t\t\t\tCalendar dcal = new GregorianCalendar();\n\t\t\t\tdcal.setTime(ap.getDate());\n\t\t\t\tCalendar ncal = new GregorianCalendar();\n\t\t\t\tncal.setTime(nt);\n\t\t\t\tncal.set(Calendar.SECOND, dcal.get(Calendar.SECOND));\n\t\t\t\tncal.set(Calendar.MINUTE, dcal.get(Calendar.MINUTE));\n\t\t\t\tncal.set(Calendar.HOUR, dcal.get(Calendar.HOUR));\n\t\t\t\tncal.set(Calendar.AM_PM, dcal.get(Calendar.AM_PM));\n\t\t\t\tnt = ncal.getTime();\n\n\t\t\t\tpl.add(Value.DATE_TIME);\n\t\t\t\tDtStart dtd = new DtStart(pl, new net.fortuna.ical4j.model.DateTime(nt));\n\t\t\t\tve.getProperties().add(dtd);\n\n\t\t\t\tVToDo todo = (VToDo) ve;\n\t\t\t\tDue due = new Due(pl, new net.fortuna.ical4j.model.DateTime(nt));\n\t\t\t\ttodo.getProperties().add(due);\n\t\t\t}\n\n\t\t} else if (AppointmentModel.isNote(ap)) {\n\t\t\tpl.add(Value.DATE);\n\t\t\tDtStart dts = new DtStart(pl, new net.fortuna.ical4j.model.Date(ap.getDate()));\n\t\t\tve.getProperties().add(dts);\n\t\t\tDate end = new Date(ap.getDate().getTime() + 1000 * 60 * 60 * 24);\n\t\t\tDtEnd dte = new DtEnd(pl, new net.fortuna.ical4j.model.Date(end));\n\t\t\tve.getProperties().add(dte);\n\t\t} else {\n\t\t\tpl.add(Value.DATE_TIME);\n\t\t\tDtStart dts = new DtStart(pl, new net.fortuna.ical4j.model.DateTime(ap.getDate()));\n\t\t\tdts.setUtc(true);\n\t\t\tve.getProperties().add(dts);\n\n\t\t\t// duration\n\t\t\tif (ap.getDuration() != null && ap.getDuration().intValue() != 0) {\n\t\t\t\t// ve.getProperties()\n\t\t\t\t// .add(new Duration(new Dur(0, 0,\n\t\t\t\t// ap.getDuration().intValue(), 0)));\n\t\t\t\tDtEnd dte = new DtEnd(pl, new net.fortuna.ical4j.model.DateTime(\n\t\t\t\t\t\tap.getDate().getTime() + 1000 * 60 * ap.getDuration().intValue()));\n\t\t\t\tdte.setUtc(true);\n\t\t\t\tve.getProperties().add(dte);\n\t\t\t}\n\t\t}\n\n\t\t// vacation is a category\n\t\tif (ap.getVacation() != null && ap.getVacation().intValue() == 1) {\n\t\t\tcatlist.add(\"Vacation\");\n\t\t} else if (ap.getVacation() != null && ap.getVacation().intValue() == 2) {\n\t\t\tcatlist.add(\"HalfDay\");\n\t\t}\n\n\t\t// holiday is a category\n\t\tif (ap.getHoliday() != null && ap.getHoliday().intValue() != 0) {\n\t\t\tcatlist.add(\"Holidays\");\n\t\t}\n\n\t\t// private\n\t\tif (ap.isPrivate()) {\n\t\t\tve.getProperties().add(Clazz.PRIVATE);\n\t\t\tcatlist.add(\"Private\");\n\t\t}\n\n\t\t// add color as a category\n\t\tif (ap.getColor() != null) {\n\t\t\tcatlist.add(\"c_\" + ap.getColor());\n\t\t}\n\n\t\tif (ap.getCategory() != null && !ap.getCategory().equals(\"\")) {\n\t\t\tcatlist.add(ap.getCategory());\n\t\t}\n\n\t\tif (ap.isTodo())\n\t\t\tcatlist.add(\"ToDo\");\n\n\t\tif (!catlist.isEmpty()) {\n\t\t\tve.getProperties().add(new Categories(catlist));\n\t\t}\n\n\t\t// repeat stuff\n\t\tif (ap.isRepeatFlag()) {\n\t\t\t// build recur string\n\t\t\tString rec = RecurrenceRule.getRRule(ap);\n\n\t\t\tve.getProperties().add(new RRule(new Recur(rec)));\n\n\t\t\t// skip list\n\t\t\tif (ap.getSkipList() != null) {\n\n\t\t\t\tlong start_epoch = DateUtil.dayOfEpoch(ap.getDate());\n\t\t\t\tif (AppointmentModel.isNote(ap)) {\n\t\t\t\t\tDateList dl = new DateList(Value.DATE);\n\t\t\t\t\tfor (String rkey : ap.getSkipList()) {\n\t\t\t\t\t\tlong skip_epoch = Long.parseLong(rkey);\n\t\t\t\t\t\tlong real_skip = ((skip_epoch - start_epoch) * 24L * 60L * 60L * 1000L)\n\t\t\t\t\t\t\t\t+ ap.getDate().getTime();\n\t\t\t\t\t\tDate skdate = new Date(real_skip);\n\t\t\t\t\t\tdl.add(new net.fortuna.ical4j.model.Date(skdate));\n\t\t\t\t\t}\n\t\t\t\t\tdl.setUtc(true);\n\t\t\t\t\tve.getProperties().add(new ExDate(dl));\n\t\t\t\t} else {\n\t\t\t\t\tDateList dl = new DateList(Value.DATE_TIME);\n\t\t\t\t\tfor (String rkey : ap.getSkipList()) {\n\t\t\t\t\t\tlong skip_epoch = Long.parseLong(rkey);\n\t\t\t\t\t\tlong real_skip = ((skip_epoch - start_epoch) * 24L * 60L * 60L * 1000L)\n\t\t\t\t\t\t\t\t+ ap.getDate().getTime();\n\t\t\t\t\t\tDateTime skdate = new DateTime(real_skip);\n\t\t\t\t\t\tdl.add(new net.fortuna.ical4j.model.DateTime(skdate));\n\t\t\t\t\t}\n\t\t\t\t\tdl.setUtc(true);\n\t\t\t\t\tve.getProperties().add(new ExDate(dl));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// reminder\n\t\tif (/* !AppointmentModel.isNote(ap) && */ap.getReminderTimes() != null && !ap.getReminderTimes().isEmpty()) {\n\n\t\t\t// add a reminder\n\t\t\tif (ap.getReminderTimes().contains(\"Y\") && (ap.isRepeatFlag() || ap.getDate().after(new Date()))) {\n\t\t\t\tVAlarm va = new VAlarm(new Dur(0, 0, -1 * 30, 0));\n\t\t\t\tva.getProperties().add(Action.DISPLAY);\n\t\t\t\tva.getProperties().add(new Description(ap.getText()));\n\t\t\t\tva.getProperties().add(new net.fortuna.ical4j.model.property.Repeat(2));\n\t\t\t\tva.getProperties().add(new Duration(new Dur(0, 0, 15, 0)));\n\t\t\t\tif (ve instanceof VEvent)\n\t\t\t\t\t((VEvent) ve).getAlarms().add(va);\n\t\t\t\telse\n\t\t\t\t\t((VToDo) ve).getAlarms().add(va);\n\n\t\t\t}\n\t\t}\n\n\t\treturn ve;\n\n\t}\n\n\tstatic private int tzOffset(long date) {\n\t\treturn TimeZone.getDefault().getOffset(date);\n\t}\n\n\tpublic static Appointment toBorg(Component comp) throws ParseException {\n\t\tif (comp instanceof VEvent || comp instanceof VToDo) {\n\n\t\t\tAppointmentModel amodel = AppointmentModel.getReference();\n\n\t\t\t// start with default appt to pull in default options\n\t\t\tAppointment ap = AppointmentModel.getDefaultAppointment();\n\t\t\tif (ap == null)\n\t\t\t\tap = amodel.newAppt();\n\n\t\t\tap.setCategory(null);\n\n\t\t\tPropertyList<Property> pl = comp.getProperties();\n\t\t\tString appttext = \"\";\n\t\t\tProperty prop = pl.getProperty(Property.SUMMARY);\n\t\t\tif (prop != null) {\n\t\t\t\tappttext += prop.getValue();\n\t\t\t}\n\n\t\t\tprop = pl.getProperty(Property.LOCATION);\n\t\t\tif (prop != null) {\n\t\t\t\tappttext += \"\\nLocation: \" + prop.getValue();\n\t\t\t}\n\n\t\t\tprop = pl.getProperty(Property.DESCRIPTION);\n\t\t\tif (prop != null) {\n\t\t\t\tappttext += \"\\n\" + prop.getValue();\n\t\t\t}\n\n\t\t\tap.setUntimed(\"Y\");\n\t\t\tap.setText(appttext);\n\t\t\tprop = pl.getProperty(Property.DTSTART);\n\n\t\t\t// for todos, use DUE over DTSTART - chg for aCalendar+\n\t\t\tif (comp instanceof VToDo) {\n\t\t\t\tProperty propdue = pl.getProperty(Property.DUE);\n\t\t\t\tif (propdue != null)\n\t\t\t\t\tprop = propdue;\n\t\t\t}\n\n\t\t\tif (prop != null) {\n\t\t\t\tDateProperty dts = (DateProperty) prop;\n\t\t\t\tDate d = dts.getDate();\n\n\t\t\t\tDate utc = new Date();\n\t\t\t\tutc.setTime(d.getTime());\n\n\t\t\t\t// adjust time zone\n\t\t\t\tif (!dts.isUtc() && !dts.getValue().contains(\"T\")) {\n\t\t\t\t\t// System.out.println( \"TZO=\" + tzOffset(d.getTime()));\n\t\t\t\t\tlong u = d.getTime() - tzOffset(d.getTime());\n\t\t\t\t\tutc.setTime(u);\n\t\t\t\t}\n\n\t\t\t\tap.setDate(utc);\n\n\t\t\t\t// check if DATE only\n\t\t\t\t// but assume appt at midnight is untimed\n\t\t\t\tif (!dts.getValue().contains(\"T\") || dts.getValue().contains(\"T000000\")) {\n\t\t\t\t\t// date only\n\t\t\t\t\tap.setUntimed(\"Y\");\n\t\t\t\t} else {\n\t\t\t\t\tap.setUntimed(\"N\");\n\t\t\t\t\tprop = pl.getProperty(Property.DTEND);\n\t\t\t\t\tif (prop != null) {\n\t\t\t\t\t\tDtEnd dte = (DtEnd) prop;\n\t\t\t\t\t\tDate de = dte.getDate();\n\t\t\t\t\t\tlong dur = (de.getTime() - d.getTime()) / (1000 * 60);\n\t\t\t\t\t\tap.setDuration(Integer.valueOf((int) dur));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tUid uid = pl.getProperty(Property.UID);\n\t\t\t// if no uid - create one - mainly can happen on ics import - not from caldav\n\t\t\tif (uid == null) {\n\t\t\t\tap.setUid(\"@NOUID-\" + UUID.randomUUID());\n\t\t\t} else {\n\t\t\t\tap.setUid(uid.getValue());\n\t\t\t}\n\n\t\t\t// store the URL coming back from the caldav server\n\t\t\t// only store the last part\n\t\t\tUrl url = pl.getProperty(Property.URL);\n\t\t\tif (url != null) {\n\t\t\t\tString urlVal = url.getValue();\n\t\t\t\tint idx = urlVal.lastIndexOf('/');\n\t\t\t\tif (idx == -1) {\n\t\t\t\t\tap.setUrl(urlVal);\n\t\t\t\t} else {\n\t\t\t\t\tap.setUrl(urlVal.substring(idx + 1));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tLastModified lm = pl.getProperty(Property.LAST_MODIFIED);\n\t\t\tif (lm != null)\n\t\t\t\tap.setLastMod(lm.getDateTime());\n\t\t\telse\n\t\t\t\tap.setLastMod(new Date());\n\t\t\tCreated cr = pl.getProperty(Property.CREATED);\n\t\t\tif (cr != null)\n\t\t\t\tap.setCreateTime(cr.getDateTime());\n\t\t\telse\n\t\t\t\tap.setCreateTime(new Date());\n\t\t\tprop = pl.getProperty(Property.DURATION);\n\t\t\tif (prop != null) {\n\t\t\t\tDuration dur = (Duration) prop;\n\n\t\t\t\tint durdays = dur.getDuration().getDays();\n\t\t\t\t// skip the the duration if >= 1 day\n\t\t\t\t// not much else we can do about it right now without\n\t\t\t\t// getting\n\t\t\t\t// really complicated\n\t\t\t\tif (durdays < 1) {\n\t\t\t\t\tap.setDuration(Integer.valueOf(dur.getDuration().getMinutes()));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tprop = pl.getProperty(Property.CATEGORIES);\n\t\t\tif (prop != null) {\n\t\t\t\tCategories cats = (Categories) prop;\n\t\t\t\tTextList catlist = cats.getCategories();\n\t\t\t\tIterator<String> cit = catlist.iterator();\n\t\t\t\twhile (cit.hasNext()) {\n\t\t\t\t\tString cat = cit.next();\n\t\t\t\t\tif (cat.equals(\"Holidays\")) {\n\t\t\t\t\t\tap.setHoliday(Integer.valueOf(1));\n\t\t\t\t\t} else if (cat.equals(\"Vacation\")) {\n\t\t\t\t\t\tap.setVacation(Integer.valueOf(1));\n\t\t\t\t\t} else if (cat.equals(\"HalfDay\")) {\n\t\t\t\t\t\tap.setVacation(Integer.valueOf(2));\n\t\t\t\t\t} else if (cat.equals(\"Private\")) {\n\t\t\t\t\t\tap.setPrivate(true);\n\t\t\t\t\t} else if (cat.equals(\"ToDo\")) {\n\t\t\t\t\t\tap.setTodo(true);\n\t\t\t\t\t} else if (cat.startsWith(\"c_\")) {\n\t\t\t\t\t\tap.setColor(cat.substring(2));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tap.setCategory(cat);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprop = pl.getProperty(Property.CLASS);\n\t\t\tif (prop != null) {\n\t\t\t\tClazz clazz = (Clazz) prop;\n\t\t\t\tif (clazz.getValue().equals(Clazz.PRIVATE.getValue())) {\n\t\t\t\t\tap.setPrivate(true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (comp instanceof VToDo) {\n\n\t\t\t\tap.setTodo(true);\n\n\t\t\t\tprop = pl.getProperty(Property.STATUS);\n\t\t\t\tif (prop != null) {\n\t\t\t\t\tStatus stat = (Status) prop;\n\t\t\t\t\tif (stat.equals(Status.VTODO_COMPLETED)) {\n\t\t\t\t\t\tap.setTodo(false);\n\t\t\t\t\t\tap.setColor(\"strike\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprop = pl.getProperty(Property.RRULE);\n\t\t\tif (prop != null) {\n\t\t\t\tRRule rr = (RRule) prop;\n\t\t\t\tRecur recur = rr.getRecur();\n\n\t\t\t\tRecurrenceRule.setRecur(ap, recur);\n\n\t\t\t\tExDate ex = pl.getProperty(Property.EXDATE);\n\t\t\t\tif (ex != null) {\n\n\t\t\t\t\tVector<String> vect = new Vector<String>();\n\n\t\t\t\t\t// add the current appt key to the SKip list\n\t\t\t\t\tDateList dl = ex.getDates();\n\t\t\t\t\tdl.setUtc(true);\n\t\t\t\t\t@SuppressWarnings(\"rawtypes\")\n\t\t\t\t\tIterator it = dl.iterator();\n\t\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\t\tObject o = it.next();\n\t\t\t\t\t\tif (o instanceof net.fortuna.ical4j.model.Date) {\n\t\t\t\t\t\t\tint rkey = (int) (((net.fortuna.ical4j.model.Date) o).getTime() / 1000 / 60 / 60 / 24);\n\t\t\t\t\t\t\tvect.add(Integer.toString(rkey));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tap.setSkipList(vect);\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn ap;\n\n\t\t}\n\t\treturn null;\n\n\t}\n\n\tstatic public CalendarComponent toIcal(Project t, boolean export_todos) throws Exception {\n\t\tif (TaskModel.isClosed(t))\n\t\t\treturn null;\n\n\t\tDate due = t.getDueDate();\n\t\tif (due == null)\n\t\t\treturn null;\n\n\t\tCalendarComponent ve = null;\n\t\tif (export_todos)\n\t\t\tve = new VToDo();\n\t\telse\n\t\t\tve = new VEvent();\n\n\t\tlong updated = new Date().getTime();\n\t\tString uidval = t.getKey() + \"@BORGP\" + updated;\n\t\tUid uid = new Uid(uidval);\n\t\tve.getProperties().add(uid);\n\n\t\t// add text\n\t\tve.getProperties().add(new Summary(\"[P]\" + t.getDescription()));\n\n\t\tParameterList pl = new ParameterList();\n\t\tpl.add(Value.DATE);\n\t\tDtStart dts = new DtStart(pl, new net.fortuna.ical4j.model.Date(due));\n\t\tve.getProperties().add(dts);\n\n\t\tDate end = new Date(due.getTime() + 1000 * 60 * 60 * 24);\n\t\tDtEnd dte = new DtEnd(pl, new net.fortuna.ical4j.model.Date(end));\n\t\tve.getProperties().add(dte);\n\n\t\treturn ve;\n\n\t}\n\n\tstatic public CalendarComponent toIcal(Task t, boolean export_todos) throws Exception {\n\t\tif (TaskModel.isClosed(t))\n\t\t\treturn null;\n\n\t\tDate due = t.getDueDate();\n\t\tif (due == null)\n\t\t\treturn null;\n\n\t\tCalendarComponent ve = null;\n\t\tif (export_todos)\n\t\t\tve = new VToDo();\n\t\telse\n\t\t\tve = new VEvent();\n\n\t\tString uidval = t.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = t.getKey() + \"@BORGT-\" + t.getCreateTime().getTime();\n\t\t}\n\t\tUid uid = new Uid(uidval);\n\t\tve.getProperties().add(uid);\n\n\t\tString urlVal = t.getUrl();\n\t\tif (urlVal != null && !urlVal.isEmpty()) {\n\t\t\tUrl url = new Url();\n\t\t\turl.setValue(urlVal);\n\t\t\tve.getProperties().add(url);\n\t\t}\n\n\t\tve.getProperties().add(new Created(new DateTime(t.getCreateTime())));\n\t\tve.getProperties().add(new LastModified(new DateTime(t.getLastMod())));\n\n\t\t// add text\n\t\tve.getProperties().add(new Summary(\"[T]\" + t.getSummary()));\n\t\tif (t.getDescription() != null && !t.getDescription().isEmpty())\n\t\t\tve.getProperties().add(new Description(t.getDescription()));\n\n\t\tParameterList pl = new ParameterList();\n\t\tpl.add(Value.DATE);\n\t\tDtStart dts = new DtStart(pl, new net.fortuna.ical4j.model.Date(due));\n\t\tve.getProperties().add(dts);\n\t\tDue du = new Due(pl, new net.fortuna.ical4j.model.Date(due));\n\t\tve.getProperties().add(du);\n\n\t\tDate end = new Date(due.getTime() + 1000 * 60 * 60 * 24);\n\t\tDtEnd dte = new DtEnd(pl, new net.fortuna.ical4j.model.Date(end));\n\t\tve.getProperties().add(dte);\n\n\t\treturn ve;\n\n\t}\n\n\tstatic public CalendarComponent toIcal(Subtask t, boolean export_todos) throws Exception {\n\n\t\tif (t.getCloseDate() != null)\n\t\t\treturn null;\n\n\t\tDate due = t.getDueDate();\n\t\tif (due == null)\n\t\t\treturn null;\n\n\t\tCalendarComponent ve = null;\n\t\tif (export_todos)\n\t\t\tve = new VToDo();\n\t\telse\n\t\t\tve = new VEvent();\n\n\t\tString uidval = t.getUid();\n\t\tif (uidval == null || uidval.isEmpty()) {\n\t\t\tuidval = t.getKey() + \"@BORGS-\" + t.getCreateTime().getTime();\n\t\t}\n\t\tUid uid = new Uid(uidval);\n\t\tve.getProperties().add(uid);\n\n\t\tString urlVal = t.getUrl();\n\t\tif (urlVal != null && !urlVal.isEmpty()) {\n\t\t\tUrl url = new Url();\n\t\t\ttry {\n\t\t\t\turl.setValue(urlVal);\n\t\t\t\tve.getProperties().add(url);\n\t\t\t} catch( URISyntaxException ue){\n\t\t\t\t// could be google junk in the url column, so ignore\n\t\t\t\tErrmsg.logError(ue);\n\t\t\t}\n\n\t\t}\n\n\t\tve.getProperties().add(new Created(new DateTime(t.getCreateTime())));\n\t\tve.getProperties().add(new LastModified(new DateTime(t.getLastMod())));\n\n\t\t// add text\n\t\tve.getProperties().add(new Summary(\"[S]\" + t.getDescription()));\n\n\t\tParameterList pl = new ParameterList();\n\t\tpl.add(Value.DATE);\n\t\tDtStart dts = new DtStart(pl, new net.fortuna.ical4j.model.Date(due));\n\t\tve.getProperties().add(dts);\n\t\tDue du = new Due(pl, new net.fortuna.ical4j.model.Date(due));\n\t\tve.getProperties().add(du);\n\n\t\tDate end = new Date(due.getTime() + 1000 * 60 * 60 * 24);\n\t\tDtEnd dte = new DtEnd(pl, new net.fortuna.ical4j.model.Date(end));\n\t\tve.getProperties().add(dte);\n\n\t\treturn ve;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/sync/ical/ICal.java",
    "content": "package net.sf.borg.model.sync.ical;\n\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport net.fortuna.ical4j.data.CalendarBuilder;\nimport net.fortuna.ical4j.data.CalendarOutputter;\nimport net.fortuna.ical4j.model.Calendar;\nimport net.fortuna.ical4j.model.Component;\nimport net.fortuna.ical4j.model.ComponentList;\nimport net.fortuna.ical4j.model.Property;\nimport net.fortuna.ical4j.model.PropertyList;\nimport net.fortuna.ical4j.model.component.CalendarComponent;\nimport net.fortuna.ical4j.model.property.ProdId;\nimport net.fortuna.ical4j.model.property.Version;\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport net.fortuna.ical4j.validate.ValidationException;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.IOHelper;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\n\npublic class ICal {\n\n\tstatic {\n\t\tSystem.setProperty(\"net.fortuna.ical4j.timezone.cache.impl\", \"net.fortuna.ical4j.util.MapTimeZoneCache\");\n\t}\n\n\tstatic public void exportApptsToFileByYear(String dir) throws Exception {\n\n\t\tCollection<Appointment> appts = AppointmentModel.getReference().getAllAppts();\n\t\tif (appts.isEmpty())\n\t\t\treturn;\n\n\t\tHashMap<Integer, List<Appointment>> apptMap = new HashMap<Integer, List<Appointment>>();\n\n\t\t// put all appts into a tree by year\n\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\tfor (Appointment ap : appts) {\n\n\t\t\tcal.setTime(ap.getDate());\n\t\t\tint year = cal.get(java.util.Calendar.YEAR);\n\t\t\tif (!apptMap.containsKey(year)) {\n\t\t\t\tList<Appointment> l = new ArrayList<Appointment>();\n\t\t\t\tapptMap.put(year, l);\n\t\t\t}\n\n\t\t\tapptMap.get(year).add(ap);\n\t\t}\n\n\t\t// start the output zip file\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\");\n\t\tString uniq = sdf.format(new Date());\n\n\t\tString backupFilename = dir + \"/borg_ics\" + uniq + \".zip\";\n\t\tZipOutputStream out = new ZipOutputStream(new FileOutputStream(backupFilename));\n\t\tWriter fw = new OutputStreamWriter(out, StandardCharsets.UTF_8);\n\n\t\t// put out the years\n\t\tfor (Integer year : apptMap.keySet()) {\n\n\t\t\tout.putNextEntry(new ZipEntry(year + \".ics\"));\n\n\t\t\tComponentList<CalendarComponent> clist = new ComponentList<CalendarComponent>();\n\n\t\t\tfor (Appointment ap : apptMap.get(year)) {\n\t\t\t\tap.setUid(\"\");\n\t\t\t\tap.setUrl(\"\");\n\t\t\t\tCalendarComponent ve = EntityIcalAdapter.toIcal(ap, true);\n\t\t\t\tif (ve != null)\n\t\t\t\t\tclist.add(ve);\n\t\t\t}\n\t\t\t\n\t\t\tPropertyList<Property> pl = new PropertyList<Property>();\n\t\t\tpl.add(new ProdId(\"BORG Calendar\"));\n\t\t\tpl.add(Version.VERSION_2_0);\n\t\t\tnet.fortuna.ical4j.model.Calendar ical = new net.fortuna.ical4j.model.Calendar(pl, clist);\n\t\t\tical.validate();\n\t\t\tfw.write(ical.toString());\n\t\t\tfw.flush();\n\t\t\tout.closeEntry();\n\t\t}\n\n\t\tout.close();\n\n\t\t\n\t}\n\n\tstatic public void exportIcalToFile(String filename, Date after) throws Exception {\n\t\tCalendar cal = exportIcal(after);\n\t\tOutputStream oostr = IOHelper.createOutputStream(filename);\n\t\tCalendarOutputter op = new CalendarOutputter();\n\t\top.output(cal, oostr);\n\t\toostr.close();\n\t}\n\n\tstatic public String exportIcalToString(Date after) throws Exception {\n\t\tCalendar cal = exportIcal(after);\n\t\tCalendarOutputter op = new CalendarOutputter();\n\t\tStringWriter sw = new StringWriter();\n\t\top.output(cal, sw);\n\t\treturn sw.toString();\n\t}\n\n\tstatic public Calendar exportIcal(Date after) throws Exception {\n\n\t\tComponentList<CalendarComponent> clist = new ComponentList<CalendarComponent>();\n\n\t\texportAppointments(clist, after);\n\t\texportTasks(clist);\n\t\texportSubTasks(clist);\n\t\texportProjects(clist);\n\n\t\tPropertyList<Property> pl = new PropertyList<Property>();\n\t\tpl.add(new ProdId(\"BORG Calendar\"));\n\t\tpl.add(Version.VERSION_2_0);\n\t\tnet.fortuna.ical4j.model.Calendar cal = new net.fortuna.ical4j.model.Calendar(pl, clist);\n\n\t\tcal.validate();\n\n\t\treturn cal;\n\t}\n\n\tstatic private void exportAppointments(ComponentList<CalendarComponent> clist, Date after) throws Exception {\n\t\tboolean export_todos = Prefs.getBoolPref(PrefName.ICAL_EXPORT_TODO);\n\n\t\tfor (Appointment ap : AppointmentModel.getReference().getAllAppts()) {\n\n\t\t\t// limit by date\n\t\t\tif (after != null) {\n\t\t\t\tDate latestInstance = Repeat.calculateLastRepeat(ap);\n\t\t\t\tif (latestInstance != null && latestInstance.before(after))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tCalendarComponent ve = EntityIcalAdapter.toIcal(ap, export_todos);\n\t\t\tif (ve != null)\n\t\t\t\tclist.add(ve);\n\n\t\t}\n\n\t}\n\n\tstatic private void exportTasks(ComponentList<CalendarComponent> clist) throws Exception {\n\n\t\tboolean export_todos = Prefs.getBoolPref(PrefName.ICAL_EXPORT_TODO);\n\n\t\tfor (Task t : TaskModel.getReference().getTasks()) {\n\t\t\tCalendarComponent c = EntityIcalAdapter.toIcal(t, export_todos);\n\t\t\tif (c != null)\n\t\t\t\tclist.add(c);\n\n\t\t}\n\n\t}\n\n\tstatic private void exportProjects(ComponentList<CalendarComponent> clist) throws Exception {\n\n\t\tboolean export_todos = Prefs.getBoolPref(PrefName.ICAL_EXPORT_TODO);\n\n\t\tfor (Project t : TaskModel.getReference().getProjects()) {\n\t\t\tCalendarComponent c = EntityIcalAdapter.toIcal(t, export_todos);\n\t\t\tif (c != null)\n\t\t\t\tclist.add(c);\n\n\t\t}\n\t}\n\n\tstatic private void exportSubTasks(ComponentList<CalendarComponent> clist) throws Exception {\n\n\t\tboolean export_todos = Prefs.getBoolPref(PrefName.ICAL_EXPORT_TODO);\n\n\t\tfor (Subtask t : TaskModel.getReference().getSubTasks()) {\n\t\t\tCalendarComponent c = EntityIcalAdapter.toIcal(t, export_todos);\n\t\t\tif (c != null)\n\t\t\t\tclist.add(c);\n\n\t\t}\n\t}\n\n\tstatic private void setHints() {\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);\n\t}\n\n\tstatic public String importIcalFromUrl(String urlString) throws Exception {\n\n\t\tsetHints();\n\n\t\tCalendarBuilder builder = new CalendarBuilder();\n\t\tURI uri = new URI(urlString);\n\t\tURL url = uri.toURL();\n\t\tInputStream is = url.openStream();\n\t\tCalendar cal = builder.build(is);\n\t\tis.close();\n\n\t\treturn importIcal(cal);\n\t}\n\n\tstatic public String importIcalFromFile(String file) throws Exception {\n\n\t\tsetHints();\n\n\t\tCalendarBuilder builder = new CalendarBuilder();\n\t\tInputStream is = new FileInputStream(file);\n\t\tCalendar cal = builder.build(is);\n\t\tis.close();\n\n\t\treturn importIcal(cal);\n\t}\n\n\tstatic private String importIcal(Calendar cal) throws Exception {\n\n\t\tint skipped = 0;\n\t\tStringBuffer dups = new StringBuffer();\n\n\t\tsetHints();\n\n\t\tStringBuffer warning = new StringBuffer();\n\n\t\ttry {\n\t\t\tcal.validate();\n\t\t} catch (ValidationException e) {\n\t\t\tErrmsg.getErrorHandler().notice(\"Ical4j validation error: \" + e.getLocalizedMessage());\n\t\t}\n\n\t\tArrayList<Appointment> aplist = new ArrayList<Appointment>();\n\n\t\tAppointmentModel amodel = AppointmentModel.getReference();\n\t\tComponentList<CalendarComponent> clist = cal.getComponents();\n\t\tIterator<CalendarComponent> it = clist.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tComponent comp = it.next();\n\n\t\t\tAppointment ap = EntityIcalAdapter.toBorg(comp);\n\t\t\tif (ap != null)\n\t\t\t\taplist.add(ap);\n\n\t\t}\n\n\t\tint imported = 0;\n\t\tint dup_count = 0;\n\n\t\tfor (Appointment ap : aplist) {\n\t\t\t// check for dups\n\t\t\tList<Appointment> appts = AppointmentModel.getReference().getAppointmentsByText(ap.getText());\n\n\t\t\tif (appts.contains(ap)) {\n\t\t\t\tdup_count++;\n\t\t\t\tdups.append(\"DUP: \" + ap.getText() + \"\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\timported++;\n\t\t\tamodel.saveAppt(ap);\n\t\t}\n\n\t\twarning.append(\"Imported: \" + imported + \"\\n\");\n\t\twarning.append(\"Skipped: \" + skipped + \"\\n\");\n\t\twarning.append(\"Duplicates: \" + dup_count + \"\\n\");\n\t\twarning.append(dups);\n\n\t\tif (warning.length() == 0)\n\t\t\treturn (null);\n\n\t\treturn (warning.toString());\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/AddressUndoItem.java",
    "content": "/*\ngetResourceString\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.model.undo;\n\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.entity.Address;\n\n\n/**\n * Address Undo Item.\n */\npublic class AddressUndoItem extends UndoItem<Address> {\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\n\t */\n\t@Override\n\tpublic void executeUndo() throws Exception {\n\t\tif (action == ActionType.DELETE) {\n\t\t\tAddressModel.getReference().saveAddress(item, true);\n\t\t} else if (action == ActionType.UPDATE) {\n\t\t\tAddressModel.getReference().saveAddress(item, true);\n\t\t} else if (action == ActionType.ADD) {\n\t\t\tAddressModel.getReference().delete(item, true);\n\t\t}\n\t}\n\n\t/**\n\t * Instantiates a new address undo item.\n\t */\n\tprivate AddressUndoItem() {\n\t  // empty\n\t}\n\n\t/**\n\t * get an identifier string for an address (human readable).\n\t * \n\t * @param addr the addr\n\t * \n\t * @return the string\n\t */\n\tstatic private String addrString(Address addr) {\n\t\treturn addr.getFirstName() + \" \" + addr.getLastName();\n\t}\n\n\t/**\n\t * Create an Address update.\n\t * \n\t * @param addr the addr\n\t * \n\t * @return the address undo item\n\t */\n\tpublic static AddressUndoItem recordUpdate(Address addr) {\n\t\tAddressUndoItem undoItem = new AddressUndoItem();\n\t\tundoItem.item = addr;\n\t\tundoItem.action = ActionType.UPDATE;\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"Address\") + \" \"\n\t\t\t\t+ addrString(addr));\n\t\treturn undoItem;\n\t}\n\n\t/**\n\t * Create an Address add.\n\t * \n\t * @param addr the addr\n\t * \n\t * @return the address undo item\n\t */\n\tpublic static AddressUndoItem recordAdd(Address addr) {\n\t\tAddressUndoItem undoItem = new AddressUndoItem();\n\t\tundoItem.item = addr;\n\t\tundoItem.action = ActionType.ADD;\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"Address\") + \" \"\n\t\t\t\t+ addrString(addr));\n\t\treturn undoItem;\n\t}\n\n\t/**\n\t * Create an Address delete.\n\t * \n\t * @param addr the addr\n\t * \n\t * @return the address undo item\n\t */\n\tpublic static AddressUndoItem recordDelete(Address addr) {\n\t\tAddressUndoItem undoItem = new AddressUndoItem();\n\t\tundoItem.item = addr;\n\t\tundoItem.action = ActionType.DELETE;\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"Address\") + \" \"\n\t\t\t\t+ addrString(addr));\n\t\treturn undoItem;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/AppointmentUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.AppointmentModel;\r\nimport net.sf.borg.model.entity.Appointment;\r\n\r\nimport java.text.DateFormat;\r\n\r\n/**\r\n * Appointment Undo Item.\r\n */\r\npublic class AppointmentUndoItem extends UndoItem<Appointment> {\r\n\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\tif (action == ActionType.DELETE) {\r\n\t\t\tAppointmentModel.getReference().saveAppt(item, true);\r\n\t\t} else if (action == ActionType.UPDATE) {\r\n\t\t\tAppointmentModel.getReference().saveAppt(item, true);\r\n\t\t} else if (action == ActionType.ADD) {\r\n\t\t\tAppointmentModel.getReference().delAppt(item, true);\r\n\t\t} \r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new appointment undo item.\r\n\t */\r\n\tprivate AppointmentUndoItem() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * get a human readable appointment string for this undo item.\r\n\t * \r\n\t * @param appt the appt\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String apptString(Appointment appt) {\r\n\t\tString txt = (appt.getText().length() < 20) ? appt.getText() : appt\r\n\t\t\t\t.getText().substring(0, 19);\r\n\t\tif( appt.isEncrypted())\r\n\t\t\ttxt = Resource.getResourceString(\"EncryptedItemShort\");\r\n\t\treturn \"[\"\r\n\t\t\t\t+ DateFormat.getDateInstance(DateFormat.SHORT).format(\r\n\t\t\t\t\t\tappt.getDate()) + \"] \" + txt;\r\n\t}\r\n\r\n\t/**\r\n\t * Record an appointment update.\r\n\t * \r\n\t * @param appt the appt\r\n\t * \r\n\t * @return the appointment undo item\r\n\t */\r\n\tpublic static AppointmentUndoItem recordUpdate(Appointment appt) {\r\n\t\tAppointmentUndoItem undoItem = new AppointmentUndoItem();\r\n\t\tundoItem.item = appt;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"appointment\") + \" \"\r\n\t\t\t\t+ apptString(appt));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record an appointment  add.\r\n\t * \r\n\t * @param appt the appt\r\n\t * \r\n\t * @return the appointment undo item\r\n\t */\r\n\tpublic static AppointmentUndoItem recordAdd(Appointment appt) {\r\n\t\tAppointmentUndoItem undoItem = new AppointmentUndoItem();\r\n\t\tundoItem.item = appt;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"appointment\") + \" \"\r\n\t\t\t\t+ apptString(appt));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record  an appointment delete.\r\n\t * \r\n\t * @param appt the appt\r\n\t * \r\n\t * @return the appointment undo item\r\n\t */\r\n\tpublic static AppointmentUndoItem recordDelete(Appointment appt) {\r\n\t\tAppointmentUndoItem undoItem = new AppointmentUndoItem();\r\n\t\tundoItem.item = appt;\r\n\t\tundoItem.action = ActionType.DELETE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"appointment\") + \" \"\r\n\t\t\t\t+ apptString(appt));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/CheckListUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.CheckListModel;\r\nimport net.sf.borg.model.entity.CheckList;\r\n\r\n/**\r\n * CheckList Undo Item.\r\n */\r\npublic class CheckListUndoItem extends UndoItem<CheckList> {\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\tif( action == ActionType.DELETE )\r\n\t\t{\r\n\t\t\tCheckListModel.getReference().saveCheckList(item, true);\r\n\t\t}\r\n\t\telse if( action == ActionType.UPDATE )\r\n\t\t{\r\n\t\t\tCheckListModel.getReference().saveCheckList(item, true);\r\n\t\t}\r\n\t\telse if( action == ActionType.ADD )\r\n\t\t{\r\n\t\t\tCheckListModel.getReference().delete(item.getCheckListName(), true);\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * Instantiates a new checkList undo item.\r\n\t */\r\n\tprivate CheckListUndoItem()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\t\r\n\t/**\r\n\t * get a human readable string for this item.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String checkListString(CheckList checkList)\r\n\t{\r\n\t\treturn checkList.getCheckListName(); \r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a checkList update.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t * \r\n\t * @return the checkList undo item\r\n\t */\r\n\tpublic static CheckListUndoItem recordUpdate(CheckList checkList)\r\n\t{\r\n\t\tCheckListUndoItem undoItem = new CheckListUndoItem();\r\n\t\tundoItem.item = checkList;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \" + \r\n\t\t\t\tResource.getResourceString(\"CheckList\") + \" \" + checkListString(checkList));\r\n\t\treturn undoItem;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a checkList add.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t * \r\n\t * @return the checkList undo item\r\n\t */\r\n\tpublic static CheckListUndoItem recordAdd(CheckList checkList)\r\n\t{\r\n\t\tCheckListUndoItem undoItem = new CheckListUndoItem();\r\n\t\tundoItem.item = checkList;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \" + \r\n\t\t\t\tResource.getResourceString(\"checkList\") + \" \" + checkListString(checkList));\r\n\t\treturn undoItem;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a checkList delete.\r\n\t * \r\n\t * @param checkList the checkList\r\n\t * \r\n\t * @return the checkList undo item\r\n\t */\r\n\tpublic static CheckListUndoItem recordDelete(CheckList checkList)\r\n\t{\r\n\t\tCheckListUndoItem undoItem = new CheckListUndoItem();\r\n\t\tundoItem.item = checkList;\r\n\t\tundoItem.action = ActionType.DELETE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \" +\r\n\t\t\t\tResource.getResourceString(\"checkList\") + \" \" + checkListString(checkList));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/MemoUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.MemoModel;\r\nimport net.sf.borg.model.entity.Memo;\r\n\r\n/**\r\n * Memo Undo Item.\r\n */\r\npublic class MemoUndoItem extends UndoItem<Memo> {\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\tif( action == ActionType.DELETE )\r\n\t\t{\r\n\t\t\tMemoModel.getReference().saveMemo(item, true);\r\n\t\t}\r\n\t\telse if( action == ActionType.UPDATE )\r\n\t\t{\r\n\t\t\tMemoModel.getReference().saveMemo(item, true);\r\n\t\t}\r\n\t\telse if( action == ActionType.ADD )\r\n\t\t{\r\n\t\t\tMemoModel.getReference().delete(item.getMemoName(), true);\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * Instantiates a new memo undo item.\r\n\t */\r\n\tprivate MemoUndoItem()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\t\r\n\t/**\r\n\t * get a human readable string for this item.\r\n\t * \r\n\t * @param memo the memo\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String memoString(Memo memo)\r\n\t{\r\n\t\treturn memo.getMemoName(); \r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a memo update.\r\n\t * \r\n\t * @param memo the memo\r\n\t * \r\n\t * @return the memo undo item\r\n\t */\r\n\tpublic static MemoUndoItem recordUpdate(Memo memo)\r\n\t{\r\n\t\tMemoUndoItem undoItem = new MemoUndoItem();\r\n\t\tundoItem.item = memo;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \" + \r\n\t\t\t\tResource.getResourceString(\"memo\") + \" \" + memoString(memo));\r\n\t\treturn undoItem;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a memo add.\r\n\t * \r\n\t * @param memo the memo\r\n\t * \r\n\t * @return the memo undo item\r\n\t */\r\n\tpublic static MemoUndoItem recordAdd(Memo memo)\r\n\t{\r\n\t\tMemoUndoItem undoItem = new MemoUndoItem();\r\n\t\tundoItem.item = memo;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \" + \r\n\t\t\t\tResource.getResourceString(\"memo\") + \" \" + memoString(memo));\r\n\t\treturn undoItem;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Record a memo delete.\r\n\t * \r\n\t * @param memo the memo\r\n\t * \r\n\t * @return the memo undo item\r\n\t */\r\n\tpublic static MemoUndoItem recordDelete(Memo memo)\r\n\t{\r\n\t\tMemoUndoItem undoItem = new MemoUndoItem();\r\n\t\tundoItem.item = memo;\r\n\t\tundoItem.action = ActionType.DELETE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \" +\r\n\t\t\t\tResource.getResourceString(\"memo\") + \" \" + memoString(memo));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/ProjectUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.TaskModel;\r\nimport net.sf.borg.model.entity.Project;\r\n\r\n/**\r\n * Project Undo Item.\r\n */\r\npublic class ProjectUndoItem extends UndoItem<Project> {\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\ttry {\r\n\t\t\tif (action == ActionType.DELETE) {\r\n\t\t\t\tTaskModel.getReference().saveProject(item, true);\r\n\t\t\t} else if (action == ActionType.UPDATE) {\r\n\t\t\t\tTaskModel.getReference().saveProject(item, true);\r\n\t\t\t} else if (action == ActionType.ADD) {\r\n\t\t\t\t\tTaskModel.getReference().deleteProject(item.getKey());\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new project undo item.\r\n\t */\r\n\tprivate ProjectUndoItem() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * human readable string for this item.\r\n\t * \r\n\t * @param st the project\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String itemString(Project st) {\r\n\t\treturn st.getDescription();\r\n\t}\r\n\r\n\t/**\r\n\t * Record a project update.\r\n\t * \r\n\t * @param project the project\r\n\t * \r\n\t * @return the project undo item\r\n\t */\r\n\tpublic static ProjectUndoItem recordUpdate(Project project) {\r\n\t\tProjectUndoItem undoItem = new ProjectUndoItem();\r\n\t\tundoItem.item = project;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"project\") + \" \"\r\n\t\t\t\t+ itemString(project));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record a project add.\r\n\t * \r\n\t * @param project the project\r\n\t * \r\n\t * @return the project undo item\r\n\t */\r\n\tpublic static ProjectUndoItem recordAdd(Project project) {\r\n\t\tProjectUndoItem undoItem = new ProjectUndoItem();\r\n\t\tundoItem.item = project;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"project\") + \" \"\r\n\t\t\t\t+ itemString(project));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/* undo of project delete is not supported */\r\n\t\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/SubtaskUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.TaskModel;\r\nimport net.sf.borg.model.entity.Subtask;\r\n\r\n/**\r\n * Subtask Undo Item.\r\n */\r\npublic class SubtaskUndoItem extends UndoItem<Subtask> {\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\ttry {\r\n\t\t\tif (action == ActionType.DELETE) {\r\n\t\t\t\tTaskModel.getReference().saveSubTask(item, true);\r\n\t\t\t} else if (action == ActionType.UPDATE) {\r\n\t\t\t\tTaskModel.getReference().saveSubTask(item, true);\r\n\t\t\t} else if (action == ActionType.ADD) {\r\n\t\t\t\tTaskModel.getReference().deleteSubTask(item.getKey(),true);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new subtask undo item.\r\n\t */\r\n\tprivate SubtaskUndoItem() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * human readable string for this item.\r\n\t * \r\n\t * @param st the subtask\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String itemString(Subtask st) {\r\n\t\treturn st.getDescription();\r\n\t}\r\n\r\n\t/**\r\n\t * Record a subtask update.\r\n\t * \r\n\t * @param subtask the subtask\r\n\t * \r\n\t * @return the subtask undo item\r\n\t */\r\n\tpublic static SubtaskUndoItem recordUpdate(Subtask subtask) {\r\n\t\tSubtaskUndoItem undoItem = new SubtaskUndoItem();\r\n\t\tundoItem.item = subtask;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"subtask\") + \" \"\r\n\t\t\t\t+ itemString(subtask));\r\n\t\t\r\n\t\tTaskUndoItem ti = TaskUndoItem.getLastTaskItem();\r\n\t\tif( ti != null )\r\n\t\t\tti.addSubtask(undoItem);\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record a subtask add.\r\n\t * \r\n\t * @param subtask the subtask\r\n\t * \r\n\t * @return the subtask undo item\r\n\t */\r\n\tpublic static SubtaskUndoItem recordAdd(Subtask subtask) {\r\n\t\tSubtaskUndoItem undoItem = new SubtaskUndoItem();\r\n\t\tundoItem.item = subtask;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"subtask\") + \" \"\r\n\t\t\t\t+ itemString(subtask));\r\n\t\tTaskUndoItem ti = TaskUndoItem.getLastTaskItem();\r\n\t\tif( ti != null )\r\n\t\t\tti.addSubtask(undoItem);\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record a subtask delete.\r\n\t * \r\n\t * @param subtask the subtask\r\n\t * \r\n\t * @return the subtask undo item\r\n\t */\r\n\tpublic static SubtaskUndoItem recordDelete(Subtask subtask) {\r\n\t\tSubtaskUndoItem undoItem = new SubtaskUndoItem();\r\n\t\tundoItem.item = subtask;\r\n\t\tundoItem.action = ActionType.DELETE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"subtask\") + \" \"\r\n\t\t\t\t+ itemString(subtask));\r\n\t\tTaskUndoItem ti = TaskUndoItem.getLastTaskItem();\r\n\t\tif( ti != null )\r\n\t\t\tti.addSubtask(undoItem);\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/TaskUndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport net.sf.borg.common.Errmsg;\r\nimport net.sf.borg.common.Resource;\r\nimport net.sf.borg.model.TaskModel;\r\nimport net.sf.borg.model.entity.Project;\r\nimport net.sf.borg.model.entity.Task;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.Stack;\r\n\r\n/**\r\n * Task Undo Item.\r\n */\r\npublic class TaskUndoItem extends UndoItem<Task> {\r\n\r\n\t// collection of subtask undo items\r\n\t// subtasks are children of tasks and when a task gets deleted, they are also deleted\r\n\t// the subtask undo items are stored as children inside the task ndo item so that\r\n\t// the user need not see undo items for every subtask. \r\n\t/** The subtasks. */\r\n\tprivate final Collection<SubtaskUndoItem> subtasks = new ArrayList<SubtaskUndoItem>();\r\n\r\n\t/* (non-Javadoc)\r\n\t * @see net.sf.borg.model.undo.UndoItem#executeUndo()\r\n\t */\r\n\t@Override\r\n\tpublic void executeUndo() {\r\n\t\ttry {\r\n\t\t\tif (action == ActionType.DELETE) {\r\n\t\t\t\t\r\n\t\t\t\t// check if parent project exists\r\n\t\t\t\tInteger pid = item.getProject();\r\n\t\t\t\tif( pid != null )\r\n\t\t\t\t{\r\n\t\t\t\t\tProject p = TaskModel.getReference().getProject(pid.intValue());\r\n\t\t\t\t\tif( p == null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// if the project has been deleted\r\n\t\t\t\t\t\t// and we are adding back a task - we need to set\r\n\t\t\t\t\t\t// its parent to null\r\n\t\t\t\t\t\titem.setProject(null);\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t// recreate the task\r\n\t\t\t\tTaskModel.getReference().savetask(item, true);\r\n\t\t\t\t\r\n\t\t\t\t// add back the subtasks\r\n\t\t\t\tfor (SubtaskUndoItem s : subtasks)\r\n\t\t\t\t{\r\n\t\t\t\t\ts.item.setTask(Integer.valueOf(item.getKey()));\r\n\t\t\t\t\ts.executeUndo();\r\n\t\t\t\t}\r\n\t\t\t} else if (action == ActionType.UPDATE) {\r\n\t\t\t\t\r\n\t\t\t\t// BORG updates subtasks during the same transaction that updates the\r\n\t\t\t\t// task, so rolling back a task needs to include a rollback of any subtask\r\n\t\t\t\t// updates\r\n\t\t\t\tfor (SubtaskUndoItem s : subtasks)\r\n\t\t\t\t{\r\n\t\t\t\t\ts.executeUndo();\r\n\t\t\t\t}\r\n\t\t\t\tTaskModel.getReference().savetask(item, true);\r\n\t\t\t} else if (action == ActionType.ADD) {\r\n\t\t\t\t\r\n\t\t\t\t// we don't need to delete the subtasks separately\r\n\t\t\t\t// there is a cascading delete when the task is deleted\r\n\t\t\t\t\tTaskModel.getReference().delete(item.getKey(), true);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Instantiates a new task undo item.\r\n\t */\r\n\tprivate TaskUndoItem() {\r\n\t  // empty\r\n\t}\r\n\r\n\t/**\r\n\t * human readable string for this item.\r\n\t * \r\n\t * @param st the Task\r\n\t * \r\n\t * @return the string\r\n\t */\r\n\tstatic private String itemString(Task st) {\r\n\t\treturn st.getSummary();\r\n\t}\r\n\r\n\t/**\r\n\t * Record a Task update.\r\n\t * \r\n\t * @param task the task\r\n\t * \r\n\t * @return the task undo item\r\n\t */\r\n\tpublic static TaskUndoItem recordUpdate(Task task) {\r\n\t\tTaskUndoItem undoItem = new TaskUndoItem();\r\n\t\tundoItem.item = task;\r\n\t\tundoItem.action = ActionType.UPDATE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Change\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"task\") + \" \"\r\n\t\t\t\t+ itemString(task));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record a Task add.\r\n\t * \r\n\t * @param task the task\r\n\t * \r\n\t * @return the task undo item\r\n\t */\r\n\tpublic static TaskUndoItem recordAdd(Task task) {\r\n\t\tTaskUndoItem undoItem = new TaskUndoItem();\r\n\t\tundoItem.item = task;\r\n\t\tundoItem.action = ActionType.ADD;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Add\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"task\") + \" \"\r\n\t\t\t\t+ itemString(task));\r\n\t\treturn undoItem;\r\n\t}\r\n\r\n\t/**\r\n\t * Record a Task delete.\r\n\t * \r\n\t * @param task the task\r\n\t * \r\n\t * @return the task undo item\r\n\t */\r\n\tpublic static TaskUndoItem recordDelete(Task task) {\r\n\t\tTaskUndoItem undoItem = new TaskUndoItem();\r\n\t\tundoItem.item = task;\r\n\t\tundoItem.action = ActionType.DELETE;\r\n\t\tundoItem.setDescription(Resource.getResourceString(\"Delete\") + \" \"\r\n\t\t\t\t+ Resource.getResourceString(\"task\") + \" \"\r\n\t\t\t\t+ itemString(task));\r\n\t\treturn undoItem;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Gets the last task item.\r\n\t * \r\n\t * @return the last task item\r\n\t */\r\n\tstatic TaskUndoItem getLastTaskItem()\r\n\t{\r\n\t\tStack<UndoItem<?>> items = UndoLog.getReference().getItems();\r\n\t\tfor( int idx = items.size() - 1; idx >= 0; idx-- )\r\n\t\t{\r\n\t\t\tUndoItem<?> item = items.elementAt(idx);\r\n\t\t\tif(item instanceof TaskUndoItem)\r\n\t\t\t\treturn (TaskUndoItem) item;\r\n\t\t}\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Adds a subtask undo item to this task undo item.\r\n\t * \r\n\t * @param st the subtask\r\n\t */\r\n\tvoid addSubtask(SubtaskUndoItem st)\r\n\t{\r\n\t\tsubtasks.add(st);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/UndoItem.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\n\r\nimport lombok.Data;\r\n\r\n/**\r\n * Abstract base class for holding a single item of work that can be undone.\r\n */\r\n@Data\r\npublic abstract class UndoItem<T> {\r\n\r\n\t/** The description of the event that can be undone. */\r\n\tprivate String description;\r\n\r\n\t/** The action that was taken. */\r\n\tprotected ActionType action;\r\n\r\n\t/** the KeyedEntity that can be undone */\r\n\tprotected T item;\r\n\r\n\t/**\r\n\t * actions that can be applied to an item that need to be undone\r\n\t */\r\n\tprotected enum ActionType {\r\n\r\n\t\tADD, DELETE, UPDATE\r\n\t}\r\n\r\n\t/**\r\n\t * execute the undo action on the item.\r\n\t */\r\n\tpublic abstract void executeUndo() throws Exception;\r\n\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/UndoLog.java",
    "content": "/*\r\n * This file is part of BORG.\r\n *\r\n * BORG is free software; you can redistribute it and/or modify it under the\r\n * terms of the GNU General Public License as published by the Free Software\r\n * Foundation; either version 2 of the License, or (at your option) any later\r\n * version.\r\n *\r\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU General Public License along with\r\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\r\n * Suite 330, Boston, MA 02111-1307 USA\r\n *\r\n * Copyright 2003 by Mike Berger\r\n */\r\npackage net.sf.borg.model.undo;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\nimport java.util.Stack;\r\nimport java.util.logging.Logger;\r\n\r\n/**\r\n * The UndoLog. This class maintains a stack of items that can be undone.\r\n */\r\npublic class UndoLog {\r\n\r\n\tstatic private final Logger auditLog = Logger.getLogger(\"net.sf.borg.audit\");\r\n\r\n\t/** The undo stack. */\r\n\tprivate final Stack<UndoItem<?>> undoStack = new Stack<UndoItem<?>>();\r\n\t\r\n\t/** The singleton. */\r\n\tprivate static final UndoLog singleton = new UndoLog();\r\n\t\r\n\t/**\r\n\t * get a reference to the undo log singleton.\r\n\t * \r\n\t * @return the undo log singletone\r\n\t */\r\n\tpublic static UndoLog getReference()\r\n\t{\r\n\t\treturn singleton;\r\n\t}\r\n\t\r\n\t/**\r\n\t * Instantiates a new undo log.\r\n\t */\r\n\tprivate UndoLog()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\t\r\n\t/**\r\n\t * add an undo item to the log.\r\n\t * \r\n\t * @param item the item\r\n\t */\r\n\tpublic void addItem(UndoItem<?> item)\r\n\t{\r\n\t\tauditLog.info(item.toString());\r\n\t\tundoStack.push(item);\r\n\t}\r\n\t\r\n\t/**\r\n\t * get a description of the top item on the stack.\r\n\t * \r\n\t * @return a description of the top item\r\n\t */\r\n\tpublic final String getTopItem()\r\n\t{\r\n\t\tif( undoStack.empty())\r\n\t\t\treturn null;\r\n\t\treturn undoStack.peek().getDescription();\r\n\t}\r\n\t\r\n\t/**\r\n\t * get descriptions for all items in the undo log.\r\n\t * \r\n\t * @return - a collection containing descriptions of all undo items in the order that they would be pulled off the stack\r\n\t */\r\n\tpublic Collection<String> getItemStrings()\r\n\t{\r\n\t\tList<String> strings = new ArrayList<String>();\r\n\t\tfor( UndoItem<?> item : undoStack )\r\n\t\t{\r\n\t\t\tstrings.add(0, item.getDescription());\r\n\t\t}\r\n\t\treturn strings;\r\n\t}\r\n\r\n\t/**\r\n\t * execute the top undo item on the stack and remove it from the stack.\r\n\t * @throws Exception \r\n\t */\r\n\tpublic void executeUndo() throws Exception\r\n\t{\r\n\t\tif( !undoStack.empty())\r\n\t\t{\r\n\t\t\tUndoItem<?> item = undoStack.pop();\r\n\t\t\titem.executeUndo();\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * get rid of all undo items.\r\n\t */\r\n\tpublic void clear()\r\n\t{\r\n\t\tundoStack.clear();\r\n\t}\r\n\t\r\n\t/**\r\n\t * get all of the undo items. This method has package visibility. Code outside of the undo package should not be able to get a hold of the undo items themselves\r\n\t * \r\n\t * @return - the undo Stack\r\n\t */\r\n\tStack<UndoItem<?>> getItems(){\r\n\t\treturn undoStack;\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/main/java/net/sf/borg/model/undo/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Contains classes that implement the undo feature\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/model/src/main/resources/borg_hsqldb.sql",
    "content": "\n-- \n-- Table structure for table addresses\n-- \n\nCREATE CACHED TABLE addresses (\n address_num integer default '0' NOT NULL,\n   first_name varchar(200) default NULL,\n   last_name varchar(200) default NULL,\n   nickname varchar(200) default NULL,\n   email varchar(200) default NULL,\n   screen_name varchar(200) default NULL,\n   work_phone varchar(200) default NULL,\n   home_phone varchar(200) default NULL,\n   fax varchar(200) default NULL,\n   pager varchar(200) default NULL,\n   street varchar(200) default NULL,\n   city varchar(200) default NULL,\n   state varchar(200) default NULL,\n   zip varchar(200) default NULL,\n   country varchar(200) default NULL,\n   company varchar(200) default NULL,\n   work_street varchar(200) default NULL,\n   work_city varchar(200) default NULL,\n   work_state varchar(200) default NULL,\n   work_zip varchar(200) default NULL,\n   work_country varchar(200) default NULL,\n   webpage varchar(200) default NULL,\n   notes longvarchar,\n   birthday date default NULL,\n   cell_phone varchar(200) default NULL,\n   vcard longvarchar default NULL,\n   PRIMARY KEY  (address_num)\n);\n\n-- --------------------------------------------------------\n\n-- \n-- Table structure for table appointments\n-- \n\nCREATE CACHED TABLE appointments (\n  appt_date timestamp default '1000-01-01 00:00:00' NOT NULL,\n  appt_num integer default '0' NOT NULL,\n  duration integer default NULL,\n  text longvarchar NOT NULL,\n  skip_list longvarchar,\n  next_todo date default NULL,\n  vacation integer default '0' NOT NULL,\n  holiday integer default '0' NOT NULL,\n  private integer default '0' NOT NULL,\n  times integer default '0' NOT NULL,\n  frequency varchar(20) default NULL,\n  todo integer default '0' NOT NULL,\n  color varchar(10) default NULL,\n  rpt integer default '0' NOT NULL,\n  category varchar(255) default NULL,\n  reminders longvarchar,\n  untimed char(1) default NULL,\n  encrypted char(1) default NULL,\n  repeat_until date default NULL,\n  priority integer default '5' NOT NULL,\n  create_time timestamp default '1980-01-01 00:00:00' NOT NULL,\n  lastmod timestamp default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n\n  PRIMARY KEY  (appt_num)\n);\n\nCREATE INDEX app_todo ON appointments (todo);\n\n-- --------------------------------------------------------\n\n-- \n-- Table structure for table options\n-- \n\nCREATE CACHED TABLE options (\n  name varchar(30) default '' NOT NULL,\n  value longvarchar NOT NULL,\n  PRIMARY KEY  (name)\n);\n\nCREATE CACHED TABLE projects (\n  id integer default '0' NOT NULL,\n  start_date date NOT NULL ,\n  due_date date default NULL,\n  description longvarchar NOT NULL,\n  category varchar(255) default NULL,\n  status varchar(10) default '' NOT NULL,\n  parent integer default NULL,\n  PRIMARY KEY  (id),\n  FOREIGN KEY (parent) REFERENCES projects ( id)\n     ON DELETE CASCADE\n);\n\nCREATE CACHED TABLE tasks (\n  tasknum integer default '0' NOT NULL,\n  start_date date NOT NULL ,\n  due_date date default NULL,\n  person_assigned varchar(10) default NULL,\n  priority integer default '3' NOT NULL,\n  state varchar(10) default '' NOT NULL,\n  type varchar(10) default '' NOT NULL,\n  description longvarchar,\n  resolution longvarchar,\n  category varchar(255) default NULL,\n  close_date date default NULL,\n  project integer default NULL,\n  summary longvarchar NOT NULL,\n  create_time timestamp default '1980-01-01 00:00:00' NOT NULL,\n  lastmod timestamp default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n  PRIMARY KEY  (tasknum),\n  FOREIGN KEY (project) REFERENCES projects ( id)\n     ON DELETE CASCADE\n);\n\n\nCREATE CACHED TABLE subtasks (\n  id integer default '0' NOT NULL,\n  create_date date NOT NULL ,\n  due_date date default NULL,\n  close_date date default NULL,\n  description longvarchar NOT NULL,\n  task integer default '0' NOT NULL,\n   create_time timestamp default '1980-01-01 00:00:00' NOT NULL,\n  lastmod timestamp default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n  PRIMARY KEY  (id),\n  FOREIGN KEY (task) REFERENCES tasks ( tasknum)\n     ON DELETE CASCADE\n);\n\n\nCREATE CACHED TABLE tasklog (\n  id integer default '0' NOT NULL,\n  logtime timestamp default '1000-01-01 00:00:00' NOT NULL,\n  description longvarchar NOT NULL,\n  task integer default '0' NOT NULL,\n  PRIMARY KEY ( id ),\n  FOREIGN KEY (task) REFERENCES tasks ( tasknum )\n     ON DELETE CASCADE\n);\n\nCREATE CACHED TABLE memos (\n  memoname varchar(50) NOT NULL,\n  memotext longvarchar,\n  encrypted char(1) default NULL,\n  PRIMARY KEY  (memoname)\n);\n\n\nCREATE CACHED TABLE links (\n  id integer default '0' NOT NULL,\n  linktype varchar(15) NOT NULL,\n  ownerkey integer default '0' NOT NULL,\n  ownertype varchar(15),\n  path varchar(250),\n  PRIMARY KEY  (id)\n);\n\nCREATE CACHED TABLE checklists (\n  name varchar(50) NOT NULL,\n  text longvarchar,\n  PRIMARY KEY  (name)\n);\n"
  },
  {
    "path": "BORGCalendar/model/src/main/resources/borg_sqlite.sql",
    "content": "\n-- \n-- Table structure for table addresses\n-- \n\nCREATE TABLE IF NOT EXISTS addresses (\n  address_num integer default '0' NOT NULL,\n  first_name varchar(200) default NULL,\n  last_name varchar(200) default NULL,\n  nickname varchar(200) default NULL,\n  email varchar(200) default NULL,\n  screen_name varchar(200) default NULL,\n  work_phone varchar(200) default NULL,\n  home_phone varchar(200) default NULL,\n  fax varchar(200) default NULL,\n  pager varchar(200) default NULL,\n  street varchar(200) default NULL,\n  city varchar(200) default NULL,\n  state varchar(200) default NULL,\n  zip varchar(200) default NULL,\n  country varchar(200) default NULL,\n  company varchar(200) default NULL,\n  work_street varchar(200) default NULL,\n  work_city varchar(200) default NULL,\n  work_state varchar(200) default NULL,\n  work_zip varchar(200) default NULL,\n  work_country varchar(200) default NULL,\n  webpage varchar(200) default NULL,\n  notes longvarchar,\n  birthday date default NULL,\n  cell_phone varchar(200) default NULL,\n  vcard longvarchar default NULL,\n  PRIMARY KEY  (address_num)\n);\n\n-- --------------------------------------------------------\n\n-- \n-- Table structure for table appointments\n-- \n\nCREATE TABLE IF NOT EXISTS appointments (\n  appt_date datetime default '1000-01-01 00:00:00' NOT NULL,\n  appt_num integer default '0' NOT NULL,\n  duration integer default NULL,\n  text longvarchar NOT NULL,\n  skip_list longvarchar,\n  next_todo date default NULL,\n  vacation integer default '0' NOT NULL,\n  holiday integer default '0' NOT NULL,\n  private integer default '0' NOT NULL,\n  times integer default '0' NOT NULL,\n  frequency varchar(20) default NULL,\n  todo integer default '0' NOT NULL,\n  color varchar(10) default NULL,\n  rpt integer default '0' NOT NULL,\n  category varchar(255) default NULL,\n  reminders longvarchar,\n  untimed char(1) default NULL,\n  encrypted char(1) default NULL,\n  repeat_until date default NULL,\n  priority integer default '5' NOT NULL,\n  create_time datetime default '1980-01-01 00:00:00' NOT NULL,\n  lastmod datetime default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n\n  PRIMARY KEY  (appt_num)\n);\n\nCREATE INDEX IF NOT EXISTS app_todo ON appointments (todo);\n\n-- --------------------------------------------------------\n\n-- \n-- Table structure for table options\n-- \n\nCREATE TABLE IF NOT EXISTS options (\n  name varchar(30) default '' NOT NULL,\n  value longvarchar NOT NULL,\n  PRIMARY KEY  (name)\n);\n\nCREATE TABLE IF NOT EXISTS projects (\n  id integer default '0' NOT NULL,\n  start_date date NOT NULL ,\n  due_date date default NULL,\n  description longvarchar NOT NULL,\n  category varchar(255) default NULL,\n  status varchar(10) default '' NOT NULL,\n  parent integer default NULL,\n  FOREIGN KEY (parent) REFERENCES projects ( id)\n     ON DELETE CASCADE,\n  PRIMARY KEY  (id)\n);\n\nCREATE TABLE IF NOT EXISTS tasks (\n  tasknum integer default '0' NOT NULL,\n  start_date date NOT NULL ,\n  due_date date default NULL,\n  person_assigned varchar(10) default NULL,\n  priority integer default '3' NOT NULL,\n  state varchar(10) default '' NOT NULL,\n  type varchar(10) default '' NOT NULL,\n  description longvarchar,\n  resolution longvarchar,\n  category varchar(255) default NULL,\n  close_date date default NULL,\n  project integer default NULL,\n  summary longvarchar NOT NULL,\n  create_time datetime default '1980-01-01 00:00:00' NOT NULL,\n  lastmod datetime default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n  PRIMARY KEY  (tasknum),\n  FOREIGN KEY (project) REFERENCES projects ( id)\n     ON DELETE CASCADE\n);\n\n\nCREATE TABLE IF NOT EXISTS subtasks (\n  id integer default '0' NOT NULL,\n  create_date date NOT NULL ,\n  due_date date default NULL,\n  close_date date default NULL,\n  description longvarchar NOT NULL,\n  task integer default '0' NOT NULL,\n   create_time datetime default '1980-01-01 00:00:00' NOT NULL,\n  lastmod datetime default '1980-01-01 00:00:00' NOT NULL,\n  uid longvarchar,\n  url longvarchar,\n  PRIMARY KEY  (id),\n  FOREIGN KEY (task) REFERENCES tasks ( tasknum)\n     ON DELETE CASCADE\n);\n\n\nCREATE TABLE IF NOT EXISTS tasklog (\n  id integer default '0' NOT NULL,\n  logtime datetime default '1000-01-01 00:00:00' NOT NULL,\n  description longvarchar NOT NULL,\n  task integer default '0' NOT NULL,\n  PRIMARY KEY ( id ),\n  FOREIGN KEY (task) REFERENCES tasks ( tasknum )\n     ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS memos (\n  memoname varchar(50) NOT NULL,\n  memotext longvarchar,\n  encrypted char(1) default NULL,\n  PRIMARY KEY  (memoname)\n);\n\n\nCREATE TABLE IF NOT EXISTS links (\n  id integer default '0' NOT NULL,\n  linktype varchar(15) NOT NULL,\n  ownerkey integer default '0' NOT NULL,\n  ownertype varchar(15),\n  path varchar(250),\n  PRIMARY KEY  (id)\n);\n\nCREATE TABLE IF NOT EXISTS checklists (\n  name varchar(50) NOT NULL,\n  text longvarchar,\n  PRIMARY KEY  (name)\n);\n"
  },
  {
    "path": "BORGCalendar/model/src/main/resources/task_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<TaskTypes>\n\t<TaskType>\n\t\t<Name>ISS</Name>\n\t\t<State>\n\t\t\t<Name>CLOSED</Name>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>IN_PRG</Name>\n\t\t\t<NextState>CLOSED</NextState>\n\t\t\t<NextState>DEF</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>OPEN</Name>\n\t\t\t<NextState>DEF</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>DEF</Name>\n\t\t\t<NextState>OPEN</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t</TaskType>\n\t<TaskType>\n\t\t<Name>TASK</Name>\n\t\t<State>\n\t\t\t<Name>CLOSED</Name>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>DEF</Name>\n\t\t\t<NextState>OPEN</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>IN_PRG</Name>\n\t\t\t<NextState>CLOSED</NextState>\n\t\t\t<NextState>DEF</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>OPEN</Name>\n\t\t\t<NextState>DEF</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<initialState>OPEN</initialState>\n\t</TaskType>\n\t<TaskType>\n\t\t<Name>DEFECT</Name>\n\t\t<State>\n\t\t\t<Name>OPEN</Name>\n\t\t\t<NextState>CLOSED</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>CLOSED</Name>\n\t\t\t<NextState>OPEN</NextState>\n\t\t</State>\n\t</TaskType>\n\t<TaskType>\n\t\t<Name>CODE</Name>\n\t\t<State>\n\t\t\t<Name>DEV</Name>\n\t\t\t<NextState>TEST</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>OPEN</Name>\n\t\t\t<NextState>DEV</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>TEST</Name>\n\t\t\t<NextState>CLOSED</NextState>\n\t\t\t<NextState>DEV</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>CLOSED</Name>\n\t\t\t<NextState>DEV</NextState>\n\t\t</State>\n\t</TaskType>\n\t<TaskType>\n\t\t<Name>DOC</Name>\n\t\t<State>\n\t\t\t<Name>CLOSED</Name>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>DEF</Name>\n\t\t\t<NextState>OPEN</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>RVW</Name>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t\t<NextState>BL</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>IN_PRG</Name>\n\t\t\t<NextState>DEF</NextState>\n\t\t\t<NextState>RVW</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>BL</Name>\n\t\t\t<NextState>CLOSED</NextState>\n\t\t\t<NextState>RVW</NextState>\n\t\t</State>\n\t\t<State>\n\t\t\t<Name>OPEN</Name>\n\t\t\t<NextState>DEF</NextState>\n\t\t\t<NextState>IN_PRG</NextState>\n\t\t</State>\n\t\t<initialState>OPEN</initialState>\n\t</TaskType>\n</TaskTypes>\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/CalendarQuickstart.java",
    "content": "package net.sf.borg.test;\n\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;\nimport com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.store.FileDataStoreFactory;\nimport com.google.api.services.calendar.CalendarScopes;\nimport com.google.api.services.tasks.Tasks;\nimport com.google.api.services.tasks.TasksScopes;\nimport com.google.api.services.tasks.model.Task;\nimport com.google.api.services.tasks.model.TaskList;\nimport com.google.api.services.tasks.model.TaskLists;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\n\nimport java.io.*;\nimport java.util.List;\n\n/* class to demonstarte use of Calendar events list API */\npublic class CalendarQuickstart {\n\n    /** Application name. */\n    private static final String APPLICATION_NAME = \"Google Calendar API Java Quickstart\";\n    /** Global instance of the JSON factory. */\n    private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();\n    /** Directory to store authorization tokens for this application. */\n    private static final String TOKENS_DIRECTORY_PATH = \"tokens\";\n\n    /**\n     * Global instance of the scopes required by this quickstart.\n     * If modifying these scopes, delete your previously saved tokens/ folder.\n     */\n    private static final List<String> SCOPES = List.of(CalendarScopes.CALENDAR, TasksScopes.TASKS);\n    private static final String CREDENTIALS_FILE_PATH = \"/tmp/credentials.json\";\n\n    /**\n     * Creates an authorized Credential object.\n     * @param HTTP_TRANSPORT The network HTTP Transport.\n     * @return An authorized Credential object.\n     * @throws IOException If the credentials.json file cannot be found.\n     */\n    private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {\n        // Load client secrets.\n        File f = new File(CREDENTIALS_FILE_PATH);\n        //InputStream in = CalendarQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);\n        InputStream in = new FileInputStream(f);\n        \n        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));\n\n        // Build flow and trigger user authorization request.\n        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(\n                HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)\n                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))\n                .setAccessType(\"offline\")\n                .build();\n        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();\n        Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize(\"user\");\n        //returns an authorized Credential object.\n        return credential;\n    }\n\n    public static void main(String... args) throws Exception {\n\n        DBHelper.setFactory(new JdbcDBHelper());\n        DBHelper.setController(new JdbcDBHelper());\n        DBHelper.getController().connect(\"jdbc:h2:mem:\");\n\n        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n\n/*\n        // Build a new authorized API client service.\n        Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n                .setApplicationName(APPLICATION_NAME)\n                .build();\n\n        CalendarList cals = service.calendarList().list().execute();\n        for(  CalendarListEntry c : cals.getItems()){\n            System.out.println(c.getSummary() + \" : \" + c.getId());\n        }\n\n        // List the next 10 events from the primary calendar.\n        DateTime now = new DateTime(System.currentTimeMillis());\n        Events events = service.events().list(\"primary\")\n                .setMaxResults(1000)\n                //.setTimeMin(now)\n                //.setOrderBy(\"startTime\")\n                .setSingleEvents(false)\n                .execute();\n        List<Event> items = events.getItems();\n        if (items.isEmpty()) {\n            System.out.println(\"No upcoming events found.\");\n        } else {\n            System.out.println(\"Upcoming events\");\n            for (Event event : items) {\n                DateTime start = event.getStart().getDateTime();\n                if (start == null) {\n                    start = event.getStart().getDate();\n                }\n                System.out.printf(\"%s (%s)\\n\", event.getSummary(), start);\n                System.out.printf(\"%s\\n\", event.getRecurrence());\n                System.out.printf(\"%s\\n\", event.toPrettyString());\n\n                try {\n                    Appointment ap = EntityGCalAdapter.toBorg(event);\n                    System.out.println(ap.toString());\n                    Event ev = EntityGCalAdapter.toGCalEvent(ap);\n                    System.out.println(ev.toPrettyString());\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n\n\n            }\n        }\n\n */\n\n        Tasks tserv = new Tasks.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n                .setApplicationName(APPLICATION_NAME)\n                .build();\n\n        // Print the first 10 task lists.\n        TaskLists result = tserv.tasklists().list()\n                .setMaxResults(1000)\n                .execute();\n        List<TaskList> taskLists = result.getItems();\n        if (taskLists == null || taskLists.isEmpty()) {\n            System.out.println(\"No task lists found.\");\n        } else {\n            System.out.println(\"Task lists:\");\n            for (TaskList tasklist : taskLists) {\n                System.out.printf(\"%s (%s)\\n\", tasklist.getTitle(), tasklist.getId());\n\n                com.google.api.services.tasks.model.Tasks result2 = tserv.tasks().list(tasklist.getId())\n                        .setMaxResults(1000)\n                        .execute();\n                List<Task> tasks = result2.getItems();\n                if (tasks == null || tasks.isEmpty()) {\n                    System.out.println(\"No tasks found.\");\n                } else {\n                    System.out.println(\"Tasks:\");\n                    for (Task task : tasks) {\n                        System.out.printf(\"%s\\n\", task.toPrettyString());\n                    }\n                }\n\n            }\n        }\n\n\n\n    }\n\n\n}"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/CardDavTest.java",
    "content": "package net.sf.borg.test;\n\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport net.fortuna.ical4j.vcard.VCard;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.model.sync.ical.AddressVcardAdapter;\nimport net.sf.borg.model.sync.ical.CardDav;\n\npublic class CardDavTest {\n\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n    @BeforeClass\n    public static void setUp() throws Exception {\n        ConsoleHandler ch = new ConsoleHandler();\n        ch.setLevel(Level.ALL);\n        log.addHandler(ch);\n        log.setUseParentHandlers(false);\n\n        log.setLevel(Level.ALL);\n\n\n    }\n\n  \n\n    @Test\n    public void testImport() throws Exception {\n        InputStream fin = IcalTest.class.getResourceAsStream(\"/test.vcs\");\n        List<VCard> vcards = CardDav.importVcardFromInputStream(fin);\n        Assert.assertEquals(2, vcards.size());\n        for (VCard vc : vcards) {\n            /*Address addr = */\n            AddressVcardAdapter.fromVcard(vc);\n        }\n    }\n\n    @Test\n    public void testAdapter() throws Exception {\n        InputStream fin = IcalTest.class.getResourceAsStream(\"/test2.vcs\");\n        List<VCard> vcards = CardDav.importVcardFromInputStream(fin);\n        Assert.assertEquals(1, vcards.size());\n        VCard vc = vcards.get(0);\n\n        Address addr = AddressVcardAdapter.fromVcard(vc);\n        Assert.assertEquals(\"Michael\", addr.getFirstName());\n        Assert.assertEquals(\"Berger\", addr.getLastName());\n        Assert.assertEquals(\"9081112222\", addr.getCellPhone());\n        Assert.assertEquals(\"1111111111\", addr.getHomePhone());\n        Assert.assertEquals(\"2222222222\", addr.getWorkPhone());\n        Assert.assertEquals(\"mike@mbcsoft.com\", addr.getEmail());\n        Assert.assertEquals(\"922 Main St\", addr.getStreetAddress());\n        Assert.assertEquals(\"Some Town\", addr.getCity());\n        Assert.assertEquals(\"Florida\", addr.getState());\n        Assert.assertEquals(\"12345\", addr.getZip());\n        Assert.assertEquals(\"work st\", addr.getWorkStreetAddress());\n        Assert.assertEquals(\"Piscataway\", addr.getWorkCity());\n        Assert.assertEquals(\"New Jersey\", addr.getWorkState());\n        Assert.assertEquals(\"08854\", addr.getWorkZip());\n\n        addr.setEmail(\"updated@email.com\");\n        addr.setNickname(\"dopey\");\n        addr.setZip(\"00000\");\n        addr.setCompany(\"MBCSOFT\");\n\n        String vcs = addr.getVcard();\n        addr.setVcard(null);\n\n        VCard vc2 = AddressVcardAdapter.toVcard(addr);\n        Address addr2 = AddressVcardAdapter.fromVcard(vc2);\n\n        Assert.assertEquals(\"Michael\", addr2.getFirstName());\n        Assert.assertEquals(\"Berger\", addr2.getLastName());\n        Assert.assertEquals(\"9081112222\", addr2.getCellPhone());\n        Assert.assertEquals(\"1111111111\", addr2.getHomePhone());\n        Assert.assertEquals(\"2222222222\", addr2.getWorkPhone());\n        Assert.assertEquals(\"updated@email.com\", addr2.getEmail());\n        Assert.assertEquals(\"922 Main St\", addr2.getStreetAddress());\n        Assert.assertEquals(\"Some Town\", addr2.getCity());\n        Assert.assertEquals(\"Florida\", addr2.getState());\n        Assert.assertEquals(\"00000\", addr2.getZip());\n        Assert.assertEquals(\"work st\", addr2.getWorkStreetAddress());\n        Assert.assertEquals(\"Piscataway\", addr2.getWorkCity());\n        Assert.assertEquals(\"New Jersey\", addr2.getWorkState());\n        Assert.assertEquals(\"08854\", addr2.getWorkZip());\n        Assert.assertEquals(\"dopey\", addr2.getNickname());\n        Assert.assertEquals(\"MBCSOFT\", addr2.getCompany());\n        Assert.assertEquals(\"mbcsoft.com\", addr2.getWebPage());\n\n\n        addr.setVcard(vcs);\n        VCard vc3 = AddressVcardAdapter.toVcard(addr);\n        addr2 = AddressVcardAdapter.fromVcard(vc3);\n\n        Assert.assertEquals(\"Michael\", addr2.getFirstName());\n        Assert.assertEquals(\"Berger\", addr2.getLastName());\n        Assert.assertEquals(\"9081112222\", addr2.getCellPhone());\n        Assert.assertEquals(\"1111111111\", addr2.getHomePhone());\n        Assert.assertEquals(\"2222222222\", addr2.getWorkPhone());\n        Assert.assertEquals(\"updated@email.com\", addr2.getEmail());\n        Assert.assertEquals(\"922 Main St\", addr2.getStreetAddress());\n        Assert.assertEquals(\"Some Town\", addr2.getCity());\n        Assert.assertEquals(\"Florida\", addr2.getState());\n        Assert.assertEquals(\"00000\", addr2.getZip());\n        Assert.assertEquals(\"work st\", addr2.getWorkStreetAddress());\n        Assert.assertEquals(\"Piscataway\", addr2.getWorkCity());\n        Assert.assertEquals(\"New Jersey\", addr2.getWorkState());\n        Assert.assertEquals(\"08854\", addr2.getWorkZip());\n        Assert.assertEquals(\"dopey\", addr2.getNickname());\n        Assert.assertEquals(\"MBCSOFT\", addr2.getCompany());\n        Assert.assertEquals(\"mbcsoft.com\", addr2.getWebPage());\n\n\n    }\n}"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/CheckListTest.java",
    "content": "package net.sf.borg.test;\r\n\r\nimport net.sf.borg.model.CheckListModel;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\r\nimport net.sf.borg.model.entity.CheckList;\r\nimport org.junit.AfterClass;\r\nimport org.junit.BeforeClass;\r\nimport org.junit.Test;\r\n\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\nimport static org.junit.Assert.assertEquals;\r\n\r\npublic class CheckListTest {\r\n\r\n\t@BeforeClass\r\n\tpublic static void setUp() throws Exception {\r\n\t\t// open the borg dbs - in memory\r\n\t\tDBHelper.setFactory(new JdbcDBHelper());\r\n\t\tDBHelper.setController(new JdbcDBHelper());\r\n\t\tDBHelper.getController().connect(\"jdbc:h2:mem:db;NON_KEYWORDS=value\");\r\n\t\t\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testCRUD() throws Exception\r\n\t{\r\n\t\t\r\n\t\tCheckList cl1 = new CheckList();\r\n\t\tcl1.setCheckListName(\"clname1\");\r\n\t\tCheckList.Item item1 = new CheckList.Item();\r\n\t\titem1.setText(\"itemtext1\");\r\n\t\titem1.setChecked(Boolean.FALSE);\r\n\t\tcl1.getItems().add(item1);\r\n\t\tCheckList.Item item2 = new CheckList.Item();\r\n\t\titem2.setText(\"itemtext2\");\r\n\t\titem2.setChecked(Boolean.TRUE);\r\n\t\tcl1.getItems().add(item2);\r\n\t\t\r\n\t\tCheckListModel.getReference().saveCheckList(cl1);\r\n\t\t\r\n\t\tCollection<String> names = CheckListModel.getReference().getNames();\r\n\t\tassertEquals(names.size(), 1);\r\n\t\t\r\n\t\tCheckList read1 = CheckListModel.getReference().getCheckList(\"clname1\");\r\n\t\tassertEquals(read1.getCheckListName(), \"clname1\");\r\n\t\tList<CheckList.Item> items = read1.getItems();\r\n\t\tassertEquals(2, items.size());\r\n\t\tfor( CheckList.Item item : items )\r\n\t\t{\r\n\t\t\tif( item.getText().equals(\"itemtext1\") )\r\n\t\t\t\tassertEquals(item.getChecked(), Boolean.FALSE);\r\n\t\t\telse if( item.getText().equals(\"itemtext2\") )\r\n\t\t\t\tassertEquals(item.getChecked(), Boolean.TRUE);\r\n\t\t\telse\r\n\t\t\t\tthrow new Exception(\"Unknown Item Found\");\r\n\t\t\t\t\r\n\t\t}\r\n\t\tassertEquals(items.get(0).getText(), \"itemtext1\");\r\n\t\tassertEquals(items.get(1).getText(), \"itemtext2\");\r\n\t\tassertEquals(items.get(1).getChecked(), Boolean.TRUE);\r\n\t\t\r\n\t\tCheckList.Item item3 = new CheckList.Item();\r\n\t\titem1.setText(\"itemtext3\");\r\n\t\titem1.setChecked(Boolean.TRUE);\r\n\t\tread1.getItems().add(item3);\r\n\t\t\r\n\t\tCheckListModel.getReference().saveCheckList(read1);\r\n\t\tCheckList read2 = CheckListModel.getReference().getCheckList(\"clname1\");\r\n\t\titems = read2.getItems();\r\n\t\tassertEquals(3, items.size());\r\n\t\t\r\n\t\tCheckListModel.getReference().delete(\"clname1\", false);\r\n\t\t\r\n\t\tassertEquals( CheckListModel.getReference().getCheckLists().size(), 0);\r\n\r\n\t\t\r\n\r\n\t}\r\n\t\r\n\t\r\n\t@AfterClass\r\n\tpublic static void tearDown()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/DBCompare.java",
    "content": "package net.sf.borg.test;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.entity.Appointment;\n\npublic class DBCompare {\n\n\tstatic public void main(String[] args) throws Exception {\n\n\t\tDBHelper.setFactory(new JdbcDBHelper());\n\t\tDBHelper.setController(new JdbcDBHelper());\n\t\tDBHelper.getController().connect(\"jdbc:sqlite:C:\\\\Users\\\\deskp\\\\OneDrive\\\\borg_h2/borg_sqlite.db\");\n\n\t\tCollection<Appointment> origappts = AppointmentModel.getReference().getAllAppts();\n\n\t\tDBHelper.getController().close();\n\t\tDBHelper.getController().connect(\"jdbc:sqlite:C:\\\\Users\\\\deskp\\\\.borg_db/borg_sqlite.db\");\n\n\t\tCollection<Appointment> newappts = AppointmentModel.getReference().getAllAppts();\n\n\t\tDBHelper.getController().close();\n\n\t\tSet<String> keyset = new HashSet<String>();\n\t\tfor (Appointment appt : newappts) {\n\n\t\t\tkeyset.add(apptKey(appt));\n\t\t}\n\n\t\tfor (Appointment appt : origappts) {\n\t\t\tif (!keyset.contains(apptKey(appt))) {\n\t\t\t\tSystem.out.println(appt);\n\t\t\t}\n\t\t}\n\n\t\tkeyset.clear();\n\n\t\tfor (Appointment appt : origappts) {\n\n\t\t\tkeyset.add(apptKey(appt));\n\t\t}\n\n\t\tfor (Appointment appt : newappts) {\n\t\t\tif (!keyset.contains(apptKey(appt))) {\n\t\t\t\tSystem.out.println(appt);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static String apptKey(Appointment appt) {\n\t\tif (appt.isTodo())\n\t\t\treturn \"todo-\" + appt.getText();\n\t\treturn appt.getDate() + \"-\" + appt.getText();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/DupFix.java",
    "content": "package net.sf.borg.test;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.entity.Appointment;\n\npublic class DupFix {\n\t\n\tstatic public void main(String[] args)  throws Exception {\n\t\t\n\t\tDBHelper.setFactory(new JdbcDBHelper());\n\t\tDBHelper.setController(new JdbcDBHelper());\n\t\tDBHelper.getController().connect(\"jdbc:sqlite:C:\\\\Users\\\\deskp\\\\OneDrive\\\\borg_h2/borg_sqlite.db\");\n\t\t\n\t\tCollection<Appointment> appts = AppointmentModel.getReference().getAllAppts();\n\t\t\n\t\tMap<String,Integer> keycount = new HashMap<String,Integer>();\n\t\tSet<String> dupkeys = new HashSet<String>();\n\t\t\n\t\tfor( Appointment appt : appts) {\n\t\t\t\n\t\t\tInteger count = keycount.get(apptKey(appt));\n\t\t\tif( count == null)\n\t\t\t\tkeycount.put(apptKey(appt), 1);\n\t\t\telse\n\t\t\t\tkeycount.put(apptKey(appt), count + 1);\n\t\t}\n\t\t\n\t\tfor( Entry<String,Integer> entry : keycount.entrySet() ) {\n\t\t\tif( entry.getValue() > 1)\n\t\t\t{\n\t\t\t\t//System.out.println(entry);\n\t\t\t\tdupkeys.add(entry.getKey());\n\t\t\t\t\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//System.out.println( \"xxx\" + entry);\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor( Appointment appt : appts ) {\n\t\t\tif( dupkeys.contains(apptKey(appt))) {\n\t\t\t\tif( appt.getUid() != null )\n\t\t\t\t{\n\t\t\t\t\t//System.out.println(\"Keeper-\" + appt.toString());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t//System.out.println(\"Delete-\" + appt.toString());\n\t\t\t\t\tSystem.out.println(\"Delete from appointments where appt_num == \" + appt.getKey() + \";\");\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tDBHelper.getController().close();\n\t\t\n\t}\n\t\n\tprivate static String apptKey(Appointment appt) {\n\t\treturn appt.getDate() + \"-\" + appt.getText();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/EncryptionTest.java",
    "content": "package net.sf.borg.test;\n\nimport net.sf.borg.common.EncryptionHelper;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.io.File;\n\nimport static org.junit.Assert.assertTrue;\n\npublic class EncryptionTest {\n\n\tprivate static final String location = \"keystore\";\n\tprivate static final String pw = \"1234\";\n\tprivate static final String keyname = \"borg_key\";\n\n\t@BeforeClass\n\tstatic public void setup() throws Exception {\n\t  // empty\n\t}\n\n\t@AfterClass\n\tstatic public void cleanup() {\n\t\tFile f = new File(location);\n\t\tf.delete();\n\t}\n\n\t@Test\n\tpublic void helperTest() throws Exception {\n\t\t// create key store\n\t\tEncryptionHelper.createStore(location, pw);\n\n\t\t// generate a key\n\t\tEncryptionHelper.generateKey(location, pw, keyname);\n\n\t\t// test encrypt/decrypt\n\t\tEncryptionHelper helper = new EncryptionHelper(location, pw);\n\t\tString text = \"Hello There 1234329!!!!\";\n\t\tString cipherText = helper.encrypt(text, keyname);\n\t\tString clearText = helper.decrypt(cipherText, keyname);\n\t\tassertTrue(text.equals(clearText));\n\n\t\t// export key\n\t\tString export = helper.exportKey(keyname, pw);\n\t\tSystem.out.println(export);\n\n\t\t// delete store\n\t\tFile f = new File(location);\n\t\tf.delete();\n\t\t\n\t\t// recreate store via import\n\t\tEncryptionHelper.createStore(location, pw);\n\t\tEncryptionHelper.importKey(location, export, keyname, pw);\n\t\t\n\t\t// verify import\n\t\thelper = new EncryptionHelper(location, pw);\n\t\tclearText = helper.decrypt(cipherText, keyname);\n\t\tassertTrue(text.equals(clearText));\n\t}\n\t\n\t/*\n\t * just to export keys for testing\n\t */\n\tpublic static void main(String[] args) throws Exception\n\t{\n\t\tEncryptionHelper helper = new EncryptionHelper(args[0], args[1]);\n\t\tString export = helper.exportKey(keyname, args[1]);\n\t\tSystem.out.println(export);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/ExecuteIcalExportByYear.java",
    "content": "package net.sf.borg.test;\n\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.sync.ical.ICal;\n\npublic class ExecuteIcalExportByYear {\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tSystem.setProperty(\"net.fortuna.ical4j.timezone.cache.impl\", \"net.fortuna.ical4j.util.MapTimeZoneCache\");\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);\n\n\t\t// open default db\n\t\tDBHelper.setFactory(new JdbcDBHelper());\n\t\tDBHelper.setController(new JdbcDBHelper());\n\t\tDBHelper.getController().connect(\"jdbc:sqlite:C:\\\\Users\\\\deskp\\\\My Drive\\\\borg/borg_sqlite.db\");\n\n\t\tICal.exportApptsToFileByYear(\".\");\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/GCalTest.java",
    "content": "package net.sf.borg.test;\n\nimport com.google.api.services.calendar.model.Event;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.sync.SyncLog;\nimport net.sf.borg.model.sync.google.EntityGCalAdapter;\nimport net.sf.borg.model.sync.google.GCal;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport java.util.Date;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Ignore\npublic class GCalTest {\n\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n    @BeforeClass\n    public static void setUp() throws Exception {\n\n        ConsoleHandler ch = new ConsoleHandler();\n        ch.setLevel(Level.FINE);\n        log.addHandler(ch);\n        log.setUseParentHandlers(false);\n        log.setLevel(Level.INFO);\n        DBHelper.setFactory(new JdbcDBHelper());\n        DBHelper.setController(new JdbcDBHelper());\n        DBHelper.getController().connect(\"jdbc:h2:file:C:/Users/deskp/borgut;USER=sa\");\n\n\n        Prefs.setPrefRootNode(\"net/sf/borg/test\");\n\n        Prefs.putPref(PrefName.GOOGLE_SYNC, \"true\");\n        Prefs.putPref(PrefName.GCAL_CAL_ID, \"borgtest\");\n        Prefs.putPref(PrefName.GCAL_TASKLIST_ID, \"testtasks\");\n        SyncLog.getReference();\n\n    }\n\n    @Test\n    public void sync() throws Exception{\n        GCal.getReference().sync(10,false, false);\n    }\n\n    @Test\n    public void load_db() throws Exception {\n        Date now = new Date();\n        for( int i = 0; i < 200; i++){\n            Appointment appt = AppointmentModel.getReference().newAppt();\n            appt.setDate(now);\n            appt.setText(\"appt\" + i);\n            AppointmentModel.getReference().saveAppt(appt);\n        }\n        for( int i = 0; i < 200; i++){\n            Appointment appt = AppointmentModel.getReference().newAppt();\n            appt.setDate(now);\n            appt.setText(\"task\" + i);\n            appt.setTodo(true);\n            AppointmentModel.getReference().saveAppt(appt);\n        }\n    }\n\n    @Test\n    public void overwrite() throws Exception {\n        GCal.getReference().sync(10,true, false);\n    }\n\n    @Test\n    public void singleTimedAppt()  throws Exception {\n\n\n        Appointment ap = AppointmentModel.getDefaultAppointment();\n        if( ap == null ) ap = AppointmentModel.getReference().newAppt();\n        ap.setText(\"test timed 1\");\n        ap.setUntimed(\"N\");\n        ap.setDate(new Date());\n        ap.setDuration(45);\n        AppointmentModel.getReference().saveAppt(ap);\n\n        GCal.getReference().sync(1, false, false);\n        ap = AppointmentModel.getReference().getAppointmentsByText(\"test timed 1\").get(0);\n\n        Assert.assertNotNull(ap);\n        log.info(ap.toString());\n        Assert.assertNotNull(ap.getUrl());\n        String gid = EntityGCalAdapter.getIdFromJSON(ap.getUrl());\n        Assert.assertNotNull(gid);\n\n        Event e = GCal.getReference().getEvent(gid);\n        Assert.assertNotNull(e);\n        Assert.assertEquals(\"test timed 1\", e.getSummary());\n\n        ap.setText(\"test timed 1a\");\n        AppointmentModel.getReference().saveAppt(ap);\n        GCal.getReference().sync(1, false, false);\n\n        e = GCal.getReference().getEvent(gid);\n        Assert.assertNotNull(e);\n        Assert.assertEquals(\"test timed 1a\", e.getSummary());\n\n        AppointmentModel.getReference().delAppt(ap.getKey());\n        GCal.getReference().sync(1, false, false);\n\n        e = GCal.getReference().getEvent(gid);\n        if( e != null )\n        {\n            Assert.assertEquals(\"cancelled\", e.getStatus());\n        }\n\n        //GCal.getReference().removeEvent(e.getId());\n\n\n    }\n\n    @Test\n    public void singleUnTimedAppt()  throws Exception {\n\n\n        Appointment ap = AppointmentModel.getDefaultAppointment();\n        if( ap == null ) ap = AppointmentModel.getReference().newAppt();\n        ap.setText(\"test untimed 1\");\n        ap.setUntimed(\"Y\");\n        ap.setDate(new Date());\n        AppointmentModel.getReference().saveAppt(ap);\n\n        GCal.getReference().sync(1, false, false);\n        ap = AppointmentModel.getReference().getAppointmentsByText(\"test untimed 1\").get(0);\n\n        Assert.assertNotNull(ap);\n        log.info(ap.toString());\n        Assert.assertNotNull(ap.getUrl());\n        String gid = EntityGCalAdapter.getIdFromJSON(ap.getUrl());\n        Assert.assertNotNull(gid);\n\n        Event e = GCal.getReference().getEvent(gid);\n        Assert.assertNotNull(e);\n        Assert.assertEquals(\"test untimed 1\", e.getSummary());\n\n        ap.setText(\"test untimed 1a\");\n        AppointmentModel.getReference().saveAppt(ap);\n        GCal.getReference().sync(1, false, false);\n\n        e = GCal.getReference().getEvent(gid);\n        Assert.assertNotNull(e);\n        Assert.assertEquals(\"test untimed 1a\", e.getSummary());\n\n        AppointmentModel.getReference().delAppt(ap.getKey());\n        GCal.getReference().sync(1, false, false);\n\n        e = GCal.getReference().getEvent(gid);\n        if( e != null )\n        {\n            Assert.assertEquals(\"cancelled\", e.getStatus());\n        }\n\n        //GCal.getReference().removeEvent(e.getId());\n\n\n    }\n\n    @Test\n    public void singleTodo() throws Exception {\n\n        Appointment ap = AppointmentModel.getDefaultAppointment();\n        if( ap == null ) ap = AppointmentModel.getReference().newAppt();\n        ap.setText(\"test todo 2\");\n        ap.setUntimed(\"Y\");\n        ap.setDate(new Date());\n        ap.setTodo(true);\n        AppointmentModel.getReference().saveAppt(ap);\n\n        log.info(EntityGCalAdapter.toGCalTask(ap).toPrettyString());\n\n        GCal.getReference().sync(1, false, false);\n\n    }\n\n    @Test\n    public void recurringTodo() throws Exception {\n\n        Appointment ap = AppointmentModel.getDefaultAppointment();\n        if( ap == null ) ap = AppointmentModel.getReference().newAppt();\n        ap.setText(\"test todo 1\");\n        ap.setUntimed(\"Y\");\n        ap.setDate(new Date());\n        ap.setTodo(true);\n        ap.setFrequency(\"weekly\");\n        ap.setTimes(52);\n        ap.setRepeatFlag(true);\n        AppointmentModel.getReference().saveAppt(ap);\n\n        log.info(EntityGCalAdapter.toGCalTask(ap).toPrettyString());\n\n        GCal.getReference().sync(1, false, false);\n\n    }\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/GCalTest2.java",
    "content": "package net.sf.borg.test;\n\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport com.google.api.services.calendar.model.Calendar;\nimport com.google.api.services.calendar.model.CalendarListEntry;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.sync.google.GCal;\n\n@Ignore\npublic class GCalTest2 {\n\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n    @BeforeClass\n    public static void setUp() throws Exception {\n\n        ConsoleHandler ch = new ConsoleHandler();\n        ch.setLevel(Level.FINE);\n        log.addHandler(ch);\n        log.setUseParentHandlers(false);\n        log.setLevel(Level.FINE);\n        //DBHelper.setFactory(new JdbcDBHelper());\n        //DBHelper.setController(new JdbcDBHelper());\n        //DBHelper.getController().connect(\"jdbc:h2:file:C:/Users/deskp/borgut;USER=sa\");\n\n\n        Prefs.setPrefRootNode(\"net/sf/borg/test\");\n\n        Prefs.putPref(PrefName.GOOGLE_SYNC, \"true\");\n        Prefs.putPref(PrefName.GCAL_CAL_ID, \"borgtest\");\n        Prefs.putPref(PrefName.GCAL_TASKLIST_ID, \"testtasks\");\n        //SyncLog.getReference();\n\n    }\n\n   @Test\n   public void listCals() throws Exception {\n\t   GCal g = GCal.getReference();\n\t   g.connect();\n\t   g.setIds();\n\t   \n\t   Calendar c = g.getCalendar(\"xx\");\n\t   log.info(c.toString());\n\t   CalendarListEntry ce = g.getCalendarListEntry(\"xx\");\n\t   log.info(ce.toPrettyString());\n\t   \n   }\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/GDriveTest.java",
    "content": "package net.sf.borg.test;\n\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.sync.google.GDrive;\n\n@Ignore\npublic class GDriveTest {\n\n    static private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n    @BeforeClass\n    public static void setUp() throws Exception {\n\n        ConsoleHandler ch = new ConsoleHandler();\n        ch.setLevel(Level.FINE);\n        log.addHandler(ch);\n        log.setUseParentHandlers(false);\n        log.setLevel(Level.FINE);\n       \n\n        Prefs.setPrefRootNode(\"net/sf/borg/test\");\n        Prefs.putPref(PrefName.GOOGLE_DB_FILE_PATH, \"\");\n        Prefs.putPref(PrefName.GOOGLE_TOKEN_DIR, \"/home/mike/borgcred/\");\n        Prefs.putPref(PrefName.GOOGLE_CRED_FILE, \"/home/mike/borgcred/xxx.json\");\n        //Prefs.putPref(PrefName.GOOGLE_SYNC, \"true\");\n        //Prefs.putPref(PrefName.GCAL_CAL_ID, \"borgtest\");\n        //Prefs.putPref(PrefName.GCAL_TASKLIST_ID, \"testtasks\");\n        //SyncLog.getReference();\n\n    }\n\n   @Test\n   public void listCals() throws Exception {\n\t   GDrive g = GDrive.getReference();\n\t   g.connect();\n\t   g.checkModTimes();\n\t   \n\t\n\t   \n   }\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/IcalAdapterTest.java",
    "content": "package net.sf.borg.test;\n\nimport net.fortuna.ical4j.model.component.CalendarComponent;\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.sync.ical.EntityIcalAdapter;\nimport org.junit.Assert;\n\nimport java.util.Collection;\n\npublic class IcalAdapterTest {\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tSystem.setProperty(\"net.fortuna.ical4j.timezone.cache.impl\", \"net.fortuna.ical4j.util.MapTimeZoneCache\");\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);\n\n\t\t// open default db\n\t\tDBHelper.setFactory(new JdbcDBHelper());\n\t\tDBHelper.setController(new JdbcDBHelper());\n\t\tDBHelper.getController().connect(\"jdbc:h2:file:~/.borg_db/borgdb;USER=sa\");\n\n\t\tCollection<Appointment> appts = AppointmentModel.getReference().getAllAppts();\n\t\tSystem.out.println(appts.size() + \" appts\");\n\t\tfor (Appointment appt : appts) {\n\n\t\t\tCalendarComponent comp = EntityIcalAdapter.toIcal(appt, true);\n\t\t\tAppointment ap2 = EntityIcalAdapter.toBorg(comp);\n\n\t\t\ttry {\n\t\t\t\tAssert.assertEquals(appt.getCategory(), ap2.getCategory());\n\t\t\t\tif (appt.getUntimed() != null && appt.getUntimed().equals(\"N\")) {\n\t\t\t\t\tAssert.assertEquals(appt.getDate().toString(), ap2.getDate().toString());\n\t\t\t\t\tAssert.assertEquals(appt.getDuration(), ap2.getDuration());\n\t\t\t\t}\n\t\t\t\tAssert.assertEquals(appt.getText(), ap2.getText());\n\t\t\t\tAssert.assertEquals(appt.getHoliday(), ap2.getHoliday());\n\t\t\t\tAssert.assertEquals(appt.isPrivate(), ap2.isPrivate());\n\t\t\t\tAssert.assertEquals(appt.isTodo(), ap2.isTodo());\n\t\t\t\tif (appt.getColor() != null)\n\t\t\t\t\tAssert.assertEquals(appt.getColor(), ap2.getColor());\n\t\t\t\tAssert.assertEquals(appt.isRepeatFlag(), ap2.isRepeatFlag());\n\t\t\t\t//Assert.assertEquals(appt.getReminderTimes(), ap2.getReminderTimes());\n\t\t\t\tif (appt.getUntimed() != null)\n\t\t\t\t\tAssert.assertEquals(appt.getUntimed(), ap2.getUntimed());\n\t\t\t\tAssert.assertEquals(appt.getPriority(), ap2.getPriority());\n\t\t\t\tAssert.assertEquals(appt.isEncrypted(), ap2.isEncrypted());\n\n\t\t\t\tif (!appt.isRepeatFlag())\n\t\t\t\t\tcontinue;\n\n\t\t\t\tAssert.assertEquals(appt.getFrequency(), ap2.getFrequency());\n\t\t\t\tAssert.assertEquals(appt.getRepeatUntil(), ap2.getRepeatUntil());\n\t\t\t\t//if (appt.isTodo())\n\t\t\t\t//\tAssert.assertEquals(appt.getNextTodo(), ap2.getNextTodo());\n\t\t\t\tAssert.assertEquals(appt.getTimes(), ap2.getTimes());\n\t\t\t\tAssert.assertEquals(appt.getSkipList(), ap2.getSkipList());\n\t\t\t\t\n\t\t\t\t//System.out.println(appt.toString());\n\t\t\t\t//System.out.println(ap2.toString());\n\t\t\t\t//System.out.println(comp.toString());\n\n\t\t\t\t\n\t\t\t} catch (Throwable e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tSystem.out.println(e.getMessage());\n\t\t\t\tSystem.out.println(appt);\n\t\t\t\tSystem.out.println(ap2.toString());\n\t\t\t\tSystem.out.println(comp);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/IcalTest.java",
    "content": "package net.sf.borg.test;\n\nimport net.fortuna.ical4j.data.CalendarBuilder;\nimport net.fortuna.ical4j.model.Calendar;\nimport net.fortuna.ical4j.model.Component;\nimport net.fortuna.ical4j.model.ComponentList;\nimport net.fortuna.ical4j.model.Property;\nimport net.fortuna.ical4j.model.component.VAlarm;\nimport net.fortuna.ical4j.model.component.VEvent;\nimport net.fortuna.ical4j.model.property.Summary;\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport org.junit.Test;\n\nimport java.io.InputStream;\n\nimport static org.junit.Assert.*;\n\npublic class IcalTest {\n\n\t@Test\n\tpublic void testParse() throws Exception {\n\t\tSystem.setProperty(\"net.fortuna.ical4j.timezone.cache.impl\", \"net.fortuna.ical4j.util.MapTimeZoneCache\");\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);\n\t\tCompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);\n\t\tInputStream fin = IcalTest.class.getResourceAsStream(\"/ap1.ics\");\n\t\tCalendarBuilder builder = new CalendarBuilder();\n\t\tCalendar calendar = builder.build(fin);\n\t\tfor (Component comp : calendar.getComponents()) {\n\t\t\tVEvent event = (VEvent) comp;\n\t\t\t// Works fine\n\t\t\tSummary desc = event.getSummary();\n\t\t\tassertNotNull(desc);\n\n\t\t\tComponentList<VAlarm> alarms = event.getAlarms();\n\t\t\tassertTrue(alarms.size() > 0);\n\t\t\t\n\t\t\tProperty p = event.getProperty(\"duh\");\n\t\t\tassertNull(p);\n\t\t\t\n\t\t\tString evstring = event.toString();\n\t\t\tSystem.out.println(evstring);\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/LinkTest.java",
    "content": "package net.sf.borg.test;\r\n\r\nimport net.sf.borg.model.*;\r\nimport net.sf.borg.model.LinkModel.LinkType;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\r\nimport net.sf.borg.model.entity.*;\r\nimport org.junit.AfterClass;\r\nimport org.junit.BeforeClass;\r\nimport org.junit.Test;\r\n\r\nimport java.util.Collection;\r\nimport java.util.Date;\r\n\r\nimport static org.junit.Assert.assertEquals;\r\n\r\npublic class LinkTest {\r\n\r\n\t@BeforeClass\r\n\tpublic static void setUp() throws Exception {\r\n\t\t// open the borg dbs - in memory\r\n\t\tDBHelper.setFactory(new JdbcDBHelper());\r\n\t\tDBHelper.setController(new JdbcDBHelper());\r\n\t\tDBHelper.getController().connect(\"jdbc:h2:mem:db;NON_KEYWORDS=value\");\r\n\t\t\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testObjectLinks() throws Exception\r\n\t{\r\n\t\t\r\n\t\tAppointment appt = new Appointment();\r\n\t\tappt.setText(\"my appointment\");\r\n\t\tappt.setDate(new Date());\r\n\t\tAppointmentModel.getReference().saveAppt(appt);\r\n\t\t\r\n\t\tTask task = new Task();\r\n\t\ttask.setDescription(\"task 1\");\r\n\t\ttask.setType(\"TASK\");\r\n\t\ttask.setStartDate(new Date());\r\n\t\ttask.setState(\"OPEN\");\r\n\t\tTaskModel.getReference().savetask(task);\r\n\t\t\r\n\t\tMemo memo = new Memo();\r\n\t\tmemo.setMemoName(\"Memo 1\");\r\n\t\tmemo.setMemoText(\"xx\");\r\n\t\tMemoModel.getReference().saveMemo(memo);\r\n\t\t\r\n\t\tAddress addr = new Address();\r\n\t\taddr.setFirstName(\"FF\");\r\n\t\taddr.setLastName(\"LL\");\r\n\t\tAddressModel.getReference().saveAddress(addr);\r\n\t\t\r\n\t\tProject project = new Project();\r\n\t\tproject.setDescription(\"Project 1\");\r\n\t\tproject.setStatus(\"OPEN\");\r\n\t\tproject.setStartDate(new Date());\r\n\t\tTaskModel.getReference().saveProject(project);\r\n\t\t\r\n\t\tLinkModel.getReference().addLink(appt, Integer.toString(task.getKey()), LinkType.TASK);\r\n\t\tLinkModel.getReference().addLink(addr, Integer.toString(task.getKey()), LinkType.TASK);\r\n\t\tLinkModel.getReference().addLink(project, Integer.toString(task.getKey()), LinkType.TASK);\r\n\r\n\t\tLinkModel.getReference().addLink(addr, Integer.toString(appt.getKey()), LinkType.APPOINTMENT);\r\n\t\tLinkModel.getReference().addLink(project, Integer.toString(appt.getKey()), LinkType.APPOINTMENT);\r\n\r\n\t\tLinkModel.getReference().addLink(project, Integer.toString(addr.getKey()), LinkType.ADDRESS);\r\n\r\n\t\tLinkModel.getReference().addLink(task, memo.getMemoName(), LinkType.MEMO);\r\n\t\tLinkModel.getReference().addLink(addr, memo.getMemoName(), LinkType.MEMO);\r\n\t\tLinkModel.getReference().addLink(appt, memo.getMemoName(), LinkType.MEMO);\r\n\t\tLinkModel.getReference().addLink(project, memo.getMemoName(), LinkType.MEMO);\r\n\t\t\r\n\t\tCollection<Link> links = LinkModel.getReference().getLinks();\r\n\t\t\r\n\t\tfor( Link link : links)\r\n\t\t\tSystem.out.println(link.toString());\r\n\t\t\r\n\t\t// number of links = 10 regular plus 6 added back links\r\n\t\tassertEquals(\"Wrong number of links\", 16, links.size());\r\n\t\t\r\n\t\tfor( KeyedEntity<?> ent : new KeyedEntity[]{ addr, appt, project, task })\r\n\t\t{\r\n\t\t\tCollection<Link> objLinks = LinkModel.getReference().getLinks(ent);\r\n\t\t\t\tassertEquals(\"Wrong number of object links\", 4, objLinks.size());\r\n\t\t}\r\n\t\t\r\n\t\tAppointmentModel.getReference().delAppt(appt);\r\n\t\t\r\n\t\tlinks = LinkModel.getReference().getLinks();\r\n\t\tassertEquals(\"Wrong number of links\", 9, links.size());\r\n\t\t\r\n\t\tfor( KeyedEntity<?> ent : new KeyedEntity[]{ addr, project, task })\r\n\t\t{\r\n\t\t\tCollection<Link> objLinks = LinkModel.getReference().getLinks(ent);\r\n\t\t\tassertEquals(\"Wrong number of object links\", 3, objLinks.size());\r\n\t\t}\r\n\t\t\r\n\t\tTaskModel.getReference().delete(task.getKey());\r\n\t\t\r\n\t\tlinks = LinkModel.getReference().getLinks();\r\n\t\tfor( Link link : links)\r\n\t\t\tSystem.out.println(\"[4] \" + link.toString());\r\n\t\tassertEquals(\"Wrong number of links\", 4, links.size());\r\n\t\t\r\n\t\tfor( KeyedEntity<?> ent : new KeyedEntity[]{ addr, project })\r\n\t\t{\r\n\t\t\tCollection<Link> objLinks = LinkModel.getReference().getLinks(ent);\r\n\t\t\tassertEquals(\"Wrong number of object links\", 2, objLinks.size());\r\n\t\t}\r\n\t\t\r\n\t\tMemoModel.getReference().delete(memo.getMemoName(), false);\r\n\t\t\r\n\t\tlinks = LinkModel.getReference().getLinks();\r\n\t\tassertEquals(\"Wrong number of links\", 2, links.size());\r\n\t\t\r\n\t\tfor( KeyedEntity<?> ent : new KeyedEntity[]{ addr, project })\r\n\t\t{\r\n\t\t\tCollection<Link> objLinks = LinkModel.getReference().getLinks(ent);\r\n\t\t\tassertEquals(\"Wrong number of object links\", 1, objLinks.size());\r\n\t\t}\r\n\t\t\r\n\t\tAddressModel.getReference().delete(addr);\r\n\t\t\r\n\r\n\t\tlinks = LinkModel.getReference().getLinks();\r\n\t\tassertEquals(\"Wrong number of links\", 0, links.size());\r\n\t\t\r\n\r\n\t}\r\n\t\r\n\t\r\n\t@AfterClass\r\n\tpublic static void tearDown()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/test/java/net/sf/borg/test/UndoTest.java",
    "content": "package net.sf.borg.test;\r\n\r\n\r\nimport net.sf.borg.model.AddressModel;\r\nimport net.sf.borg.model.AppointmentModel;\r\nimport net.sf.borg.model.MemoModel;\r\nimport net.sf.borg.model.TaskModel;\r\nimport net.sf.borg.model.db.DBHelper;\r\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\r\nimport net.sf.borg.model.entity.*;\r\nimport net.sf.borg.model.undo.UndoLog;\r\nimport org.junit.AfterClass;\r\nimport org.junit.BeforeClass;\r\nimport org.junit.Test;\r\n\r\nimport java.util.Collection;\r\nimport java.util.Date;\r\nimport java.util.Iterator;\r\n\r\nimport static org.junit.Assert.assertTrue;\r\n\r\npublic class UndoTest {\r\n\r\n\t@BeforeClass\r\n\tpublic static void setUp() throws Exception {\r\n\t\t// open the borg dbs - in memory\r\n\t\tDBHelper.setFactory(new JdbcDBHelper());\r\n\t\tDBHelper.setController(new JdbcDBHelper());\r\n\t\tDBHelper.getController().connect(\"jdbc:h2:mem:db;NON_KEYWORDS=value\");\r\n\t\t\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testAppointmentUndo() throws Exception\r\n\t{\r\n\t\tint num_appts = AppointmentModel.getReference().getAllAppts().size();\r\n\t\tassertTrue( \"Appointment DB should be empty to start\", num_appts == 0);\r\n\t\t\r\n\t\tAppointment appt = new Appointment();\r\n\t\tappt.setText(\"my appointment\");\r\n\t\tappt.setDate(new Date());\r\n\t\t\r\n\t\tAppointment appt2 = new Appointment();\r\n\t\tappt2.setText(\"my appointment 2\");\r\n\t\tappt2.setDate(new Date());\r\n\t\t\r\n\t\tAppointmentModel.getReference().saveAppt(appt);\r\n\t\tAppointmentModel.getReference().saveAppt(appt2);\r\n\t\t\r\n\t\tCollection<Appointment> coll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 2 appts\", coll.size() == 2);\r\n\t\t\r\n\t\t// update the appt\r\n\t\tIterator<Appointment> iter = coll.iterator();\r\n\t\tappt = iter.next();\r\n\t\tint id = appt.getKey(); // save the id\r\n\t\t\r\n\t\tSystem.out.println(\"Appt key = \" + id);\r\n\t\t\r\n\t\tappt.setText(\"my updated appt\");\r\n\t\tAppointmentModel.getReference().saveAppt(appt);\r\n\t\t\r\n\t\t// verify that the appt is updated\r\n\t\tcoll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 2 appts\", coll.size() == 2);\r\n\t\tappt = AppointmentModel.getReference().getAppt(id);\r\n\t\tassertTrue(\"Appointment was not updated\", \"my updated appt\".equals(appt.getText()));\r\n\t\t\r\n\t\t// delete the appt\r\n\t\tAppointmentModel.getReference().delAppt(appt);\r\n\t\tcoll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 1 appts\", coll.size() == 1);\r\n\t\t\r\n\t\t// let the undos begin\r\n\t\t\r\n\t\t// undo the delete\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the appt is the updated one\r\n\t\tcoll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 2 appts\", coll.size() == 2);\r\n\t\tappt = AppointmentModel.getReference().getAppt(id);\r\n\t\tassertTrue(\"Appointment was not updated\", \"my updated appt\".equals(appt.getText()));\r\n\t\t\r\n\t\t// undo the update\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the appt is the original one\r\n\t\tcoll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 2 appt\", coll.size() == 2);\r\n\t\tappt = AppointmentModel.getReference().getAppt(id);\r\n\t\tassertTrue(\"Appointment was not undone: \" + appt.getText(), \"my appointment\".equals(appt.getText()));\r\n\t\t\r\n\t\t//undo the add\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\tcoll = AppointmentModel.getReference().getAllAppts();\r\n\t\tassertTrue( \"Appointment DB should contain 1 appts after add undone\", coll.size() == 1);\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testAddressUndo() throws Exception\r\n\t{\r\n\t\tint num_addrs = AddressModel.getReference().getAddresses().size();\r\n\t\tassertTrue( \"Address DB should be empty to start\", num_addrs == 0);\r\n\t\t\r\n\t\tAddress addr = new Address();\r\n\t\taddr.setLastName(\"Last name\");\r\n\t\taddr.setFirstName(\"First name\");\r\n\t\t\r\n\t\tAddressModel.getReference().saveAddress(addr);\r\n\t\t\r\n\t\tCollection<Address> coll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 1 addr\", coll.size() == 1);\r\n\t\t\r\n\t\t// update the addr\r\n\t\taddr = coll.iterator().next();\r\n\t\taddr.setFirstName(\"Updated name\");\r\n\t\tAddressModel.getReference().saveAddress(addr);\r\n\t\t\r\n\t\t// verify that the addr is updated\r\n\t\tcoll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 1 addr\", coll.size() == 1);\r\n\t\taddr = coll.iterator().next();\r\n\t\tassertTrue(\"Address was not updated\", \"Updated name\".equals(addr.getFirstName()));\r\n\t\t\r\n\t\t// delete the addr\r\n\t\tAddressModel.getReference().delete(addr);\r\n\t\tcoll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 0 appts\", coll.size() == 0);\r\n\t\t\r\n\t\t// let the undos begin\r\n\t\t\r\n\t\t// undo the delete\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the addr is the updated one\r\n\t\tcoll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 1 addr\", coll.size() == 1);\r\n\t\taddr = coll.iterator().next();\r\n\t\tassertTrue(\"Address was not updated\", \"Updated name\".equals(addr.getFirstName()));\r\n\t\t\r\n\t\t// undo the update\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the addr is the original one\r\n\t\tcoll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 1 addr\", coll.size() == 1);\r\n\t\taddr = coll.iterator().next();\r\n\t\t\r\n\t\tassertTrue(\"Address was not undone\", \"First name\".equals(addr.getFirstName()));\r\n\t\t\r\n\t\t//undo the add\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\tcoll = AddressModel.getReference().getAddresses();\r\n\t\tassertTrue( \"Address DB should contain 0 appts after add undone\", coll.size() == 0);\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testMemoUndo() throws Exception\r\n\t{\r\n\t\tint num_addrs = MemoModel.getReference().getMemos().size();\r\n\t\tassertTrue( \"Address DB should be empty to start\", num_addrs == 0);\r\n\t\t\r\n\t\tMemo memo = new Memo();\r\n\t\tmemo.setMemoName(\"memo name\");\r\n\t\tmemo.setMemoText(\"memo text\");\r\n\t\t\r\n\t\tMemoModel.getReference().saveMemo(memo);\r\n\t\t\r\n\t\tCollection<Memo> coll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 1 memo\", coll.size() == 1);\r\n\t\t\r\n\t\t// update the memo\r\n\t\tmemo = coll.iterator().next();\r\n\t\tmemo.setMemoText(\"Updated text\");\r\n\t\tMemoModel.getReference().saveMemo(memo);\r\n\t\t\r\n\t\t// verify that the memo is updated\r\n\t\tcoll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 1 memo\", coll.size() == 1);\r\n\t\tmemo = coll.iterator().next();\r\n\t\tassertTrue(\"Memo was not updated\", \"Updated text\".equals(memo.getMemoText()));\r\n\t\t\r\n\t\t// delete the memo\r\n\t\tMemoModel.getReference().delete(\"memo name\", false);\r\n\t\tcoll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 0 appts\", coll.size() == 0);\r\n\t\t\r\n\t\t// let the undos begin\r\n\t\t\r\n\t\t// undo the delete\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the memo is the updated one\r\n\t\tcoll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 1 memo\", coll.size() == 1);\r\n\t\tmemo = coll.iterator().next();\r\n\t\tassertTrue(\"Memo was not updated\", \"Updated text\".equals(memo.getMemoText()));\r\n\t\t\r\n\t\t// undo the update\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the memo is the original one\r\n\t\tcoll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 1 memo\", coll.size() == 1);\r\n\t\tmemo = coll.iterator().next();\r\n\t\t\r\n\t\tassertTrue(\"Memo was not undone: \" + memo.getMemoText(), \"memo text\".equals(memo.getMemoText()));\r\n\t\t\r\n\t\t//undo the add\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\tcoll = MemoModel.getReference().getMemos();\r\n\t\tassertTrue( \"Memo DB should contain 0 appts after add undone\", coll.size() == 0);\r\n\t}\r\n\t\r\n\t@Test\r\n\tpublic void testTaskUndo() throws Exception\r\n\t{\r\n\t\tint num = TaskModel.getReference().getTasks().size();\r\n\t\tassertTrue( \"Address DB should be empty to start\", num == 0);\r\n\t\t\r\n\t\tTask task = new Task();\r\n\t\ttask.setDescription(\"task 1\");\r\n\t\ttask.setStartDate(new Date());\r\n\t\ttask.setState(\"OPEN\");\r\n\t\ttask.setType(\"CODE\");\r\n\t\tTaskModel.getReference().savetask(task);\r\n\t\t\r\n\t\tCollection<Task> coll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 1 task\", coll.size() == 1);\r\n\t\t\r\n\t\tInteger taskid = Integer.valueOf(coll.iterator().next().getKey());\r\n\t\tSubtask st1 = new Subtask();\r\n\t\tst1.setDescription(\"st1\");\r\n\t\tst1.setStartDate(new Date());\r\n\t\tst1.setTask(taskid);\r\n\t\tTaskModel.getReference().saveSubTask(st1);\r\n\t\tSubtask st2 = new Subtask();\r\n\t\tst2.setDescription(\"st2\");\r\n\t\tst2.setStartDate(new Date());\r\n\t\tst2.setTask(taskid);\r\n\t\tTaskModel.getReference().saveSubTask(st2);\r\n\t\tnum = TaskModel.getReference().getSubTasks(taskid.intValue()).size();\r\n\t\tassertTrue(\"Task does not have 2 subtasks: \" + num, num == 2);\r\n\t\t\r\n\t\t// update the task\r\n\t\ttask = coll.iterator().next();\r\n\t\ttask.setDescription(\"Updated text\");\r\n\t\tTaskModel.getReference().savetask(task);\r\n\t\t\r\n\t\t// verify that the task is updated\r\n\t\tcoll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 1 task\", coll.size() == 1);\r\n\t\ttask = coll.iterator().next();\r\n\t\tassertTrue(\"Task was not updated\", \"Updated text\".equals(task.getDescription()));\r\n\t\t\r\n\t\t// delete a subtask\r\n\t\tCollection<Subtask> scoll = TaskModel.getReference().getSubTasks(taskid.intValue());\r\n\t\tTaskModel.getReference().deleteSubTask(scoll.iterator().next().getKey());\r\n\t\tnum = TaskModel.getReference().getSubTasks(taskid.intValue()).size();\r\n\t\tassertTrue(\"Task does not have 1 subtask: \" + num, num == 1);\r\n\t\t\r\n\t\t\r\n\t\t// delete the task\r\n\t\tTaskModel.getReference().delete(taskid.intValue());\r\n\t\tcoll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 0 tasks\", coll.size() == 0);\r\n\t\t\r\n\t\t// let the undos begin\r\n\t\t\r\n\t\t// undo the delete\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the task is the updated one\r\n\t\tcoll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 1 task\", coll.size() == 1);\r\n\t\ttask = coll.iterator().next();\r\n\t\tassertTrue(\"Task was not updated\", \"Updated text\".equals(task.getDescription()));\r\n\t\tnum = TaskModel.getReference().getSubTasks(taskid.intValue()).size();\r\n\t\tassertTrue(\"Task does not have 1 subtask: \" + num, num == 1);\r\n\t\t\r\n\t\t// undo the update - should add back 1 subtask as well\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\t// verify that the task is the original one\r\n\t\tcoll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 1 task\", coll.size() == 1);\r\n\t\ttask = coll.iterator().next();\r\n\t\tassertTrue(\"Task was not undone: \" + task.getDescription(), \"task 1\".equals(task.getDescription()));\r\n\t\tnum = TaskModel.getReference().getSubTasks(taskid.intValue()).size();\r\n\t\tassertTrue(\"Task does not have 2 subtasks: \" + num, num == 2);\r\n\t\t\r\n\t\t//undo the add\r\n\t\tUndoLog.getReference().executeUndo();\r\n\t\t\r\n\t\tcoll = TaskModel.getReference().getTasks();\r\n\t\tassertTrue( \"Task DB should contain 0 tasks after add undone\", coll.size() == 0);\r\n\t}\r\n\t\r\n\t@AfterClass\r\n\tpublic static void tearDown()\r\n\t{\r\n\t\t// empty\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "BORGCalendar/model/src/test/resources/ap1.ics",
    "content": "BEGIN:VCALENDAR\nPRODID:-//MBCSoft/BORG//EN\nVERSION:2.0\nBEGIN:VEVENT\nDTSTAMP:20190824T004337Z\nUID:2@BORGA-315550800000\nCREATED:19800101T050000Z\nLAST-MODIFIED:20131122T131026Z\nSUMMARY:Wedding Anniversary\nDTSTART;VALUE=DATE:19890529\nDTEND;VALUE=DATE:19890530\nCATEGORIES:blue\nRRULE:FREQ=YEARLY\nBEGIN:VALARM\nTRIGGER:-PT30M\nACTION:DISPLAY\nDESCRIPTION:Wedding Anniversary\nREPEAT:2\nDURATION:PT15M\nEND:VALARM\nEND:VEVENT\nEND:VCALENDAR\n"
  },
  {
    "path": "BORGCalendar/model/src/test/resources/test.vcs",
    "content": "BEGIN:VCARD\nVERSION:4.0\nPRODID:-//MailClient.VObject/8.2.1659.0\nUID:urn:uuid:c3ce345a-c06e-4b0b-8a59-01e85c3ae473\nREV:20211122T141029Z\nN;SORT-AS=\"Berger, Michael\":Berger;Michael;;;\nFN:Michael  Berger\nEMAIL;PREF=1:mike@mbcsoft.com\nEMAIL:mike@comcast.net\nTEL;TYPE=CELL:9081112222\nTEL;TYPE=HOME,VOICE:1111111111\nTEL;TYPE=WORK,VOICE:2222222222\nTEL;TYPE=WORK,FAX:ffffffffff\nTEL;TYPE=PAGER:ppppp\nIMPP:ymsgr:i_flem6\nADR;TYPE=HOME:;;922 Main St;Some Town;Florida;12345;United States of\n America\nADR;TYPE=WORK:;;work st;Piscataway;New Jersey;08854;Albania\nBDAY;VALUE=date:20211122\nORG:MBCSoft;\nNOTE:notes here\nURL:mbcsoft.com\nNICKNAME:NICK\nEND:VCARD\nBEGIN:VCARD\nVERSION:4.0\nPRODID:-//MailClient.VObject/8.2.1659.0\nUID:urn:uuid:c3ce345a-c06e-8a59-8a59-01e85c3ae473\nREV:20211022T141029Z\nN;SORT-AS=\"Berger, Cathy\":Berger;Cathy;;;\nFN:Cathy  Berger\nEMAIL;PREF=1:cathy@mbcsoft.com\nEMAIL:cathy@comcast.net\nTEL;TYPE=CELL:9084442222\nTEL;TYPE=HOME,VOICE:4441111111\nTEL;TYPE=WORK,VOICE:2244422222\nTEL;TYPE=WORK,FAX:fffxxxxfff\nTEL;TYPE=PAGER:eeee\nIMPP:ymsgr:cathmsgr\nADR;TYPE=HOME:;;922 Main St;Some Town;Florida;12345;United States of\n America\nADR;TYPE=WORK:;;work st;Piscataway;New Jersey;08854;Albania\nBDAY;VALUE=date:20211122\nORG:MBCSoft;\nNOTE:notes here\nURL:mbcsoft.com\nEND:VCARD"
  },
  {
    "path": "BORGCalendar/model/src/test/resources/test2.vcs",
    "content": "BEGIN:VCARD\nVERSION:4.0\nPRODID:-//MailClient.VObject/8.2.1659.0\nUID:urn:uuid:c3ce345a-c06e-4b0b-8a59-01e85c3ae473\nREV:20211122T141029Z\nN;SORT-AS=\"Berger, Michael\":Berger;Michael;;;\nFN:Michael  Berger\nEMAIL;PREF=1:mike@mbcsoft.com\nEMAIL:mike@comcast.net\nTEL;TYPE=CELL:9081112222\nTEL;TYPE=HOME,VOICE:1111111111\nTEL;TYPE=WORK,VOICE:2222222222\nTEL;TYPE=WORK,FAX:ffffffffff\nTEL;TYPE=PAGER:ppppp\nIMPP:ymsgr:i_flem6\nADR;TYPE=HOME:;;922 Main St;Some Town;Florida;12345;United States of\n America\nADR;TYPE=WORK:;;work st;Piscataway;New Jersey;08854;Albania\nBDAY;VALUE=date:20211122\nNOTE:notes here\nURL:mbcsoft.com\nEND:VCARD\n"
  },
  {
    "path": "BORGCalendar/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<groupId>BORGCalendar</groupId>\n\t<artifactId>BORGCalendar</artifactId>\n\t<version>2.0</version>\n\t<packaging>pom</packaging>\n\t<url>http://mbcsoft.com</url>\n\t<inceptionYear>2003</inceptionYear>\n\t<organization>\n\t\t<name>Michael Berger</name>\n\t</organization>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>GNU General Public License - Version 2</name>\n\t\t\t<url>http://www.gnu.org/licenses/gpl-2.0.html</url>\n\t\t\t<distribution>repo</distribution>\n\t\t</license>\n\t</licenses>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<lombok.version>1.18.44</lombok.version>\n\t</properties>\n\n\t<modules>\n\t\t<module>common</module>\n\t\t<module>model</module>\n\t\t<module>swingui</module>\n\t\t<module>install</module>\n\t</modules>\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<!-- Project to project dependencies. -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>${project.groupId}</groupId>\n\t\t\t\t<artifactId>common</artifactId>\n\t\t\t\t<version>${project.version}</version>\n\t\t\t\t<type>jar</type>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>${project.groupId}</groupId>\n\t\t\t\t<artifactId>model</artifactId>\n\t\t\t\t<version>${project.version}</version>\n\t\t\t\t<type>jar</type>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>${project.groupId}</groupId>\n\t\t\t\t<artifactId>swingui</artifactId>\n\t\t\t\t<version>${project.version}</version>\n\t\t\t\t<type>jar</type>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>junit</groupId>\n\t\t\t\t<artifactId>junit</artifactId>\n\t\t\t\t<version>4.13.2</version>\n\t\t\t\t<scope>test</scope>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t\t<artifactId>lombok</artifactId>\n\t\t\t\t<version>${lombok.version}</version>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t<build>\n\n\t\t<plugins>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>3.7.0</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>21</source>\n\t\t\t\t\t<target>21</target>\n\t\t\t\t\t<annotationProcessorPaths>\n\t\t\t\t\t\t<path>\n\t\t\t\t\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t\t\t\t\t<artifactId>lombok</artifactId>\n\t\t\t\t\t\t\t<version>${lombok.version}</version>\n\t\t\t\t\t\t</path>\n\t\t\t\t\t</annotationProcessorPaths>\n\n\t\t\t\t</configuration>\n\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-javadoc-plugin</artifactId>\n\t\t\t\t<version>2.9.1</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<aggregate>true</aggregate>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "BORGCalendar/swingui/.gitignore",
    "content": "/target\n/.classpath\n"
  },
  {
    "path": "BORGCalendar/swingui/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>BORGCalendar</groupId>\n\t\t<artifactId>BORGCalendar</artifactId>\n\t\t<version>2.0</version>\n\t</parent>\n\t<artifactId>swingui</artifactId>\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>BORGCalendar</groupId>\n\t\t\t<artifactId>common</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>BORGCalendar</groupId>\n\t\t\t<artifactId>model</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.toedter</groupId>\n\t\t\t<artifactId>jcalendar</artifactId>\n\t\t\t<version>1.4</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>javax.help</groupId>\n\t\t\t<artifactId>javahelp</artifactId>\n\t\t\t<version>2.0.05</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.formdev</groupId>\n\t\t\t<artifactId>flatlaf</artifactId>\n\t\t\t<version>3.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.formdev</groupId>\n\t\t\t<artifactId>flatlaf-intellij-themes</artifactId>\n\t\t\t<version>3.7</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.drjekyll</groupId>\n\t\t\t<artifactId>fontchooser</artifactId>\n\t\t\t<version>3.1.0</version>\n\t\t</dependency>\n\n\n\t</dependencies>\n\t<build>\n\t\t<plugins>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-dependency-plugin</artifactId>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>copy-dependencies</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>copy-dependencies</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<outputDirectory>${project.build.directory}/lib</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t<version>2.3.2</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<archive>\n\t\t\t\t\t\t<index>false</index>\n\t\t\t\t\t\t<manifest>\n\t\t\t\t\t\t\t<addClasspath>true</addClasspath>\n\t\t\t\t\t\t\t<classpathPrefix>lib/</classpathPrefix>\n\t\t\t\t\t\t\t<mainClass>net.sf.borg.control.Borg</mainClass>\n\t\t\t\t\t\t</manifest>\n\t\t\t\t\t\t<manifestEntries>\n\t\t\t\t\t\t\t<mode>development</mode>\n\t\t\t\t\t\t\t<url>http://borg-calendar.sf.net</url>\n\t\t\t\t\t\t\t<key>value</key>\n\t\t\t\t\t\t\t<Class-Path>lib/filedrop-1.1.jar</Class-Path>\n\t\t\t\t\t\t</manifestEntries>\n\t\t\t\t\t</archive>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\n\t</build>\n</project>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/control/Borg.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.control;\n\nimport net.sf.borg.common.*;\nimport net.sf.borg.model.*;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.db.jdbc.DbDirtyManager;\nimport net.sf.borg.model.db.jdbc.JdbcDBHelper;\nimport net.sf.borg.model.sync.google.GDrive;\nimport net.sf.borg.ui.UIControl;\nimport net.sf.borg.ui.options.OptionsView;\nimport net.sf.borg.ui.util.PasswordHelper;\nimport net.sf.borg.ui.util.PwMigration;\nimport net.sf.borg.ui.util.ScrolledDialog;\nimport net.sf.borg.ui.util.UIErrorHandler;\n\nimport javax.swing.*;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Timer;\nimport java.util.*;\nimport java.util.logging.*;\n\n/**\n * The Main Class of Borg. It's responsible for starting up the model and\n * spawning various threads, including the main UI thread and various timer\n * threads. It also handles shutdown.\n */\n\npublic class Borg implements SocketServer.SocketHandler {\n\n\t/** The singleton. */\n\tstatic volatile private Borg singleton = null;\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\tstatic private final Logger auditLog = Logger.getLogger(\"net.sf.borg.audit\");\n\n\t/**\n\t * Gets the singleton.\n\t *\n\t * @return the singleton\n\t */\n\tstatic public Borg getReference() {\n\t\tif (singleton == null) {\n\t\t\tBorg b = new Borg();\n\t\t\tsingleton = b;\n\t\t}\n\t\treturn (singleton);\n\t}\n\n\t/**\n\t * The main method.\n\t *\n\t * @param args the arguments\n\t */\n\tpublic static void main(String[] args) {\n\t\t// create a new borg object and call its init routine with the command\n\t\t// line args\n\t\tBorg b = getReference();\n\t\tb.init(args);\n\t}\n\n\t/**\n\t * close db connections and end the program\n\t */\n\tstatic public void shutdown() {\n\n\t\tif (getReference().mailTimer_ != null)\n\t\t\tgetReference().mailTimer_.cancel();\n\n\t\ttry {\n\t\t\t// close the db\n\t\t\tDbDirtyManager.getReference().setEnabled(false); // don't react to the writing of the db shutdown timestamp\n\t\t\tDBHelper.getController().close();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\t// wait 3 seconds before exiting for the db to settle down - probably\n\t\t// being superstitious.\n\t\tTimer shutdownTimer = new java.util.Timer(\"ShutdownTimer\");\n\t\tshutdownTimer.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tGDrive.getReference().showUploadDialog();\n\t\t\t\tSystem.exit(0);\n\t\t\t}\n\t\t}, 3 * 1000, 28 * 60 * 1000);\n\n\t}\n\n\t/** The timer for sending reminder email. */\n\tprivate Timer mailTimer_ = null;\n\n\t/**\n\t * The socket server - listens for incoming requests such as open requests\n\t */\n\tprivate SocketServer socketServer_ = null;\n\n\t/**\n\t * constructor\n\t */\n\tprivate Borg() {\n\t\t// empty\n\t}\n\n\t/**\n\t * process a socket message\n\t */\n\t@Override\n\tpublic synchronized String processSocketMessage(String msg) {\n\t\tlog.fine(\"Got msg: \" + msg);\n\t\tif (msg.equals(\"open\")) {\n\t\t\tUIControl.toFront();\n\t\t\treturn (\"ok\");\n\t\t}\n\t\treturn (\"Unknown msg: \" + msg);\n\t}\n\n\t/**\n\t * Initialize the application\n\t *\n\t * @param args the args\n\t */\n\tprivate void init(String[] args) {\n\n\t\t// override for testing a different db\n\t\tString testdb = null;\n\n\t\t// testing flag\n\t\tboolean testing = false;\n\n\t\t// process command line args\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tif (args[i].equals(\"-db\")) {\n\t\t\t\ti++;\n\t\t\t\tif (i >= args.length) {\n\t\t\t\t\tSystem.out.println(Resource.getResourceString(\"-db_argument_is_missing\"));\n\t\t\t\t\tSystem.exit(1);\n\t\t\t\t}\n\t\t\t\ttestdb = args[i];\n\t\t\t} else if (args[i].equals(\"-test\")) {\n\t\t\t\ttesting = true;\n\t\t\t}\n\n\t\t}\n\n\t\t// if testing, use alternate prefs so regular prefs can be left alone\n\t\tif (testing) {\n\t\t\tPrefs.setPrefRootNode(\"net/sf/borg/test\");\n\t\t\tPrefs.putPref(PrefName.SUB_CACHE_FILENAME, \".borg.testcache\");\n\t\t}\n\n\t\t// logging\n\t\tConsoleHandler ch = new ConsoleHandler();\n\t\tch.setLevel(Level.ALL);\n\t\tlog.addHandler(ch);\n\t\tlog.setUseParentHandlers(false);\n\n\t\tboolean debug = Prefs.getBoolPref(PrefName.DEBUG);\n\t\tif (debug == true)\n\t\t\tlog.setLevel(Level.ALL);\n\t\telse\n\t\t\tlog.setLevel(Level.INFO);\n\n\t\tlog.fine(\"Debug logging turned on\");\n\n\t\t// open existing BORG if there is one\n\t\tint port = Prefs.getIntPref(PrefName.SOCKETPORT);\n\t\tif (port != -1) {\n\t\t\tString resp;\n\t\t\ttry {\n\t\t\t\tresp = SocketClient.sendMsg(\"localhost\", port, \"open\");\n\t\t\t\tif (resp != null && resp.equals(\"ok\")) {\n\t\t\t\t\t// if we found a running borg to open, then exit\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t} else {\n\t\t\t\t\tErrmsg.setErrorHandler(new UIErrorHandler());\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\"A program running on BORG's port [\" + port\n\t\t\t\t\t\t\t+ \"] did not respond. BORG cannot tell if it is already running.\\nShutting down.\");\n\t\t\t\t\tSystem.exit(1);\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t}\n\n\t\t// redirect stdout and stderr to files\n\t\ttry {\n\t\t\tif (!testing) {\n\t\t\t\tString home = System.getProperty(\"user.home\", \"\");\n\t\t\t\tFileOutputStream errStr = new FileOutputStream(home + \"/.borg.out\", false);\n\t\t\t\tPrintStream printStream = new PrintStream(errStr);\n\t\t\t\tSystem.setErr(printStream);\n\t\t\t\tSystem.setOut(printStream);\n\n\t\t\t\tFileHandler fh = new FileHandler(\"%h/.borg.log\");\n\t\t\t\tfh.setFormatter(new SimpleFormatter());\n\t\t\t\tfh.setLevel(ch.getLevel());\n\t\t\t\tlog.removeHandler(ch);\n\t\t\t\tlog.addHandler(fh);\n\n\t\t\t\tFileHandler fh2 = new FileHandler(\"%h/.borgaudit.log\", 1024 * 1024, 5, true);\n\t\t\t\tfh2.setFormatter(new SimpleFormatter());\n\t\t\t\tfh2.setLevel(Level.INFO);\n\t\t\t\tauditLog.addHandler(fh2);\n\n\t\t\t\tif (Prefs.getBoolPref(PrefName.AUDITLOG))\n\t\t\t\t\tauditLog.setLevel(Level.INFO);\n\t\t\t\telse\n\t\t\t\t\tauditLog.setLevel(Level.SEVERE);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\t// locale\n\t\tString country = Prefs.getPref(PrefName.COUNTRY);\n\t\tString language = Prefs.getPref(PrefName.LANGUAGE);\n\t\tif (!language.equals(\"\")) {\n\t\t\tLocale.setDefault(Locale.of(language, country));\n\t\t}\n\t\t\n\t\t\n\t\t// check for upgrade issues\n\t\tUpgradeCheck.checkUpgrade();\n\n\t\t// JDBC is only choice for now. In the future, set this based on DBType\n\t\tDBHelper.setFactory(new JdbcDBHelper());\n\t\tDBHelper.setController(new JdbcDBHelper());\n\n\t\t// db url\n\t\tString dbdir = null;\n\n\t\ttry {\n\t\t\tif (testdb != null)\n\t\t\t\tdbdir = testdb;\n\t\t\telse\n\t\t\t\tdbdir = DBHelper.getController().buildURL(); // derive db url\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// from user\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// prefs\n\n\t\t\tif (dbdir.equals(\"not-set\")) {\n\n\t\t\t\t// try to set a valid default\n\t\t\t\tString home = System.getProperty(\"user.home\", \"\");\n\t\t\t\tFile borgdir = null;\n\n\t\t\t\t// on MAC OS, store borg database into\n\t\t\t\t// $HOME/Library/BorgCalendar\n\t\t\t\tString os = System.getProperty(\"os.name\").toLowerCase();\n\t\t\t\tif (os.indexOf(\"mac\") != -1) {\n\t\t\t\t\tborgdir = new File(home, \"Library/BorgCalendar\");\n\t\t\t\t}\n\t\t\t\t// Default:, store as hidden folder inside $HOME\n\t\t\t\telse if( testing ) {\n\t\t\t\t\tborgdir = new File(home + \"/borgtestdb\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tborgdir = new File(home + \"/.borg_db\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!borgdir.exists()) {\n\t\t\t\t\tborgdir.mkdir();\n\t\t\t\t}\n\t\t\t\tif (borgdir.isDirectory() && borgdir.canWrite()) {\n\t\t\t\t\tPrefs.putPref(PrefName.SQLITEDIR, borgdir.getAbsolutePath());\n\t\t\t\t\tPrefs.putPref(PrefName.DBTYPE, \"sqlite\");\n\t\t\t\t\tdbdir = DBHelper.getController().buildURL();\n\t\t\t\t} else {\n\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"selectdb\"),\n\t\t\t\t\t\t\tResource.getResourceString(\"Notice\"), JOptionPane.INFORMATION_MESSAGE);\n\n\t\t\t\t\t// if user wants to set db - let them\n\t\t\t\t\tOptionsView.dbSelectOnly();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if the DB is on GDrive, check if GDrive is newer and if so, warn that we are\n\t\t\t// out of sync\n\t\t\ttry {\n\t\t\t\t//GDrive.getReference().connect();\n\t\t\t\tGDrive.getReference().checkModTimes();\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\tGDrive.showErrorDialog(false,e.getMessage());\n\t\t\t}\n\n\t\t\t// connect to the db - for now, it is jdbc only\n\t\t\tDBHelper.getController().connect(dbdir);\n\n\t\t\t// force models to be instantiated\n\t\t\tAppointmentModel.getReference();\n\t\t\tMemoModel.getReference();\n\t\t\tCheckListModel.getReference();\n\t\t\tAddressModel.getReference();\n\t\t\tTaskModel.getReference();\n\t\t\tLinkModel.getReference();\n\n\t\t\tPwMigration.migratePasswords();\n\n\t\t\t// start the UI thread\n\t\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tUIControl.startUI();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// calculate email time in minutes from now\n\t\t\tCalendar cal = new GregorianCalendar();\n\t\t\tint emailmins = Prefs.getIntPref(PrefName.EMAILTIME);\n\t\t\tint curmins = 60 * cal.get(Calendar.HOUR_OF_DAY) + cal.get(Calendar.MINUTE);\n\t\t\tint mailtime = emailmins - curmins;\n\t\t\tif (mailtime < 0) {\n\t\t\t\t// we are past mailtime - send it now\n\n\t\t\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (Prefs.getBoolPref(PrefName.DAILYEMAILENABLED)\n\t\t\t\t\t\t\t\t\t&& Prefs.getBoolPref(PrefName.EMAILENABLED)\n\t\t\t\t\t\t\t\t\t&& EmailReminder.needToSendDailyEmail()) {\n\t\t\t\t\t\t\t\tString passwd = PasswordHelper.getReference()\n\t\t\t\t\t\t\t\t\t\t.decryptText(Prefs.getPref(PrefName.EMAILPASS), \"Unlock Email Password\", false);\n\t\t\t\t\t\t\t\tif (passwd != null) {\n\t\t\t\t\t\t\t\t\tEmailReminder.sendDailyEmailReminder(null, passwd);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlog.info(\"Skipping Daily Email\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlog.info(\"Skipping Daily Email\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// set timer for next mailtime\n\t\t\t\tmailtime += 24 * 60; // 24 hours from now\n\t\t\t}\n\n\t\t\t// start up email check timer - every 24 hours\n\t\t\tthis.mailTimer_ = new java.util.Timer(\"MailTimer\");\n\t\t\tthis.mailTimer_.schedule(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Prefs.getBoolPref(PrefName.DAILYEMAILENABLED) && Prefs.getBoolPref(PrefName.EMAILENABLED)) {\n\t\t\t\t\t\t\tString passwd = PasswordHelper.getReference().decryptText(Prefs.getPref(PrefName.EMAILPASS),\n\t\t\t\t\t\t\t\t\t\"Unlock Email Password\", false);\n\t\t\t\t\t\t\tif (passwd != null) {\n\t\t\t\t\t\t\t\tEmailReminder.sendDailyEmailReminder(null, passwd);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlog.info(\"Skipping Daily Email\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.info(\"Skipping Daily Email\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, mailtime * 60 * 1000, 24 * 60 * 60 * 1000);\n\n\t\t\t// start socket listener\n\t\t\tif (port != -1 && this.socketServer_ == null) {\n\t\t\t\tthis.socketServer_ = new SocketServer(port, this);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\t/*\n\t\t\t * if something goes wrong, it might be that the database directory is bad.\n\t\t\t * Maybe it does not exist anymore or something, so give the user a chance to\n\t\t\t * change it if it will fix the problem\n\t\t\t */\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\n\t\t\tString es = e.toString();\n\t\t\tes += Resource.getResourceString(\"db_set_to\") + dbdir;\n\t\t\tes += Resource.getResourceString(\"bad_db_2\");\n\n\t\t\t// prompt for ok\n\t\t\tint ret = ScrolledDialog.showOptionDialog(es);\n\t\t\tif (ret == ScrolledDialog.OK) {\n\t\t\t\tOptionsView.dbSelectOnly();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tSystem.exit(1);\n\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/control/UpgradeCheck.java",
    "content": "package net.sf.borg.control;\n\nimport java.awt.BorderLayout;\nimport java.awt.FlowLayout;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JDialog;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.UIManager;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\n\npublic class UpgradeCheck {\n\n\tstatic private final PrefName DBCHECKDONE = new PrefName(\"dbcheckdone\", \"false\");\n\tstatic private final PrefName CALDAVCHECKDONE = new PrefName(\"caldavcheckdone\", \"false\");\n\tstatic private final PrefName CALDAV_SERVER = new PrefName(\"caldav-server\", \"\");\n\n\tpublic static void main(String args[]) {\n\t\t//checkUpgrade();\n\t\tshowDialogWithDismissCheckbox(\n\t\t\t\t\"<html>You are using an H2 database.<br>If your database was created by BORG 1.X, and you have not already done so,<br> you need to export the database using BORG 1.X and then import as an H2 or Sqlite\"\n\t\t\t\t\t\t+ \" database in BORG 2.0.<br>\" + \"</html>\",\n\t\t\t\t\t\tnew PrefName(\"xxx\", \"false\"), false);\n\t\tSystem.exit(0);\n\t}\n\n\t// check if BORG is upgrading and perform any upgrade related actions\n\tstatic void checkUpgrade() {\n\n\t\t/*\n\t\t * Check for Upgrade to 2.0\n\t\t */\n\n\t\t// Database Transition Warning for HSQL and H2\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\n\n\t\t// TBD\n\n\t\t// CALDAV warning\n\t\tString caldavServer = Prefs.getPref(CALDAV_SERVER);\n\t\tif (caldavServer != null && !caldavServer.isEmpty()) {\n\n\t\t\tshowDialogWithDismissCheckbox(\n\t\t\t\t\t\"<html>BORG 2.0 does not support CALDAV.<br>Please remain on BORG 1.X if you want to use CALDAV<br></html>\",\n\t\t\t\t\tCALDAVCHECKDONE, false);\n\t\t}\n\n\t\tif (dbtype.equals(\"h2\")) {\n\n\t\t\t// check for first time user. if db has never been set, never show the warning\n\t\t\tString h2dir = Prefs.getPref(PrefName.H2DIR);\n\t\t\tif (h2dir.equals(\"not-set\")) {\n\t\t\t\tPrefs.putPref(DBCHECKDONE, \"true\");\n\t\t\t} else {\n\n\t\t\t\tshowDialogWithDismissCheckbox(\n\t\t\t\t\t\t\"<html>You are using an H2 database.<br>If your database was created by BORG 1.X, and you have not already converted to 2_0<br><br>RECOMMENDED:\" \n\t\t\t\t\t    + \"<ol><li>export the database using BORG 1.X into a backup zip file</li><li>change BORG 1.X to use Sqlite as the database</li>\"\n\t\t\t\t\t\t+ \"<li>import your backup zip file into BORG 2_0</li><</ol>\"\n\t\t\t\t\t    + \"<br>If you truly want to keep H2 as your database:\"\n\t\t\t\t\t\t+ \"<ol><li>export the database using BORG 1.X into a backup zip file</li>\"\n\t\t\t\t\t\t+ \"<li>go to your database folder and rename or delete your current database file. you cannot import into an existing database</li>\"\n\t\t\t\t\t\t+ \"<li>import your backup zip file into BORG 2_0</li></ol><br>Do not proceed if you need to return to BORG 1_X\"\n\t\t\t\t\t\t+ \"</html>\",\n\t\t\t\t\t\tDBCHECKDONE, false);\n\t\t\t}\n\t\t} else if (dbtype.contains(\"hsql\")) {\n\t\t\tshowDialogWithDismissCheckbox(\n\t\t\t\t\t\"<html>You are using an HSQLDB database.<br>BORG 2.0 does not support this.<br>You should: <ol><li>export the database using BORG 1.X into a backup zip file</li><li>change BORG 1.X to use Sqlite as the database</li>\"\n\t\t\t\t\t+ \"<li>import your backup zip file into BORG 1_X</li><li>Run BORG 2_0</li></ol><br><br> DO NOT PROCEEED<br>\" + \"</html>\",\n\t\t\t\t\tDBCHECKDONE, true);\n\t\t} else\n\t\t\tPrefs.putPref(DBCHECKDONE, \"true\");\n\n\n\t}\n\n\tprivate static void showDialogWithDismissCheckbox(String text, PrefName prefName, boolean mustExit) {\n\n\t\t// check if already seen and dismissed\n\t\tif (Prefs.getBoolPref(prefName))\n\t\t\treturn;\n\n\t\t/*\n\t\t * gemini wrote 99% of the following, including the comments\n\t\t */\n\n\t\t// Create the main frame (or just use null for parent if not needed)\n\t\t// We'll use a hidden JFrame as the owner to properly center the dialog.\n\t\tJFrame ownerFrame = new JFrame();\n\t\townerFrame.setSize(0, 0); // Keep it invisible\n\t\townerFrame.setVisible(false);\n\n\n\t\tfinal JDialog dialog = new JDialog(ownerFrame, \"Upgrade Warning\", true);\n\t\tdialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);\n\t\t\n\t\tdialog.addWindowListener(new WindowAdapter() {\n\t\t    @Override\n\t\t    public void windowClosing(WindowEvent e) {\n\t\t        System.exit(0); // Force the application to close\n\t\t    }\n\t\t});\n\n\t\t// 2. Create the main content panel\n\t\tJPanel contentPanel = new JPanel();\n\t\tcontentPanel.setLayout(new BorderLayout(20, 20)); // Padding between components\n\t\tcontentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20)); // Overall padding\n\n\t\t// --- Text Area ---\n\t\tJLabel textLabel = new JLabel(text);\n\t\t// textLabel.setFont(new Font(\"SansSerif\", Font.PLAIN, 12));\n\n\t\t// Use a wrapper panel for the text to mimic a JOptionPane-style layout\n\t\t// (optional)\n\t\tJPanel textPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));\n\t\ttextPanel.add(textLabel);\n\t\tcontentPanel.add(textPanel, BorderLayout.CENTER);\n\n\t\t// --- Checkbox Area ---\n\t\tfinal JCheckBox dontShowAgain = new JCheckBox(\"Do not show this again\");\n\t\tdontShowAgain.setFocusPainted(false);\n\t\tif (!mustExit)\n\t\t\tcontentPanel.add(dontShowAgain, BorderLayout.SOUTH);\n\n\t\t// --- Button Panel ---\n\t\tJPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));\n\n\t\t// 3. Create \"Exit the program\" button\n\t\tIcon exitIcon = UIManager.getIcon(\"OptionPane.informationIcon\"); // Using a standard information icon\n\t\tif (exitIcon == null) {\n\t\t\tSystem.out.println(\"Could not find UIManager information icon, falling back to a dummy icon.\");\n\t\t\texitIcon = new ImageIcon(); // Fallback\n\t\t}\n\t\tJButton exitButton = new JButton(\"Exit the program\", exitIcon);\n\t\texitButton.addActionListener(e -> {\n\t\t\t// Get the checkbox state before exiting\n\t\t\tif (dontShowAgain.isSelected()) {\n\t\t\t\tSystem.out.println(\"User chose 'Do not show this again' before exiting.\");\n\t\t\t\tPrefs.putPref(prefName, \"true\");\n\t\t\t}\n\t\t\tSystem.out.println(\"Exiting the program.\");\n\t\t\tSystem.exit(0); // Terminate the application\n\t\t});\n\n\t\t// 4. Create \"Proceed Anyway\" button with the stop sign icon\n\n\t\t// A. Get a red stop sign icon (from system icons if available, or load one)\n\t\t// Using UIManager to get a standard icon for demonstration, or load from file.\n\t\t// For a more robust solution, you might load an image file:\n\t\t// Icon stopIcon = new ImageIcon(\"path/to/stop_sign.png\");\n\n\t\t// For standard icons, we can try using a built-in one like error/warning icon:\n\t\tIcon stopIcon = UIManager.getIcon(\"OptionPane.errorIcon\");\n\t\tif (stopIcon == null) {\n\t\t\t// Fallback if the UIManager doesn't provide a suitable icon (less common)\n\t\t\tSystem.out.println(\"Could not find UIManager icon, falling back to a dummy icon.\");\n\t\t\tstopIcon = new ImageIcon(); // Empty icon\n\t\t}\n\n\t\tJButton proceedButton = new JButton(\"Proceed Anyway\", stopIcon);\n\t\t// Optional: Set the button to be the default action\n\t\tdialog.getRootPane().setDefaultButton(proceedButton);\n\n\t\tproceedButton.addActionListener(e -> {\n\t\t\t// Get the checkbox state\n\t\t\tSystem.out.println(\"User chose 'Proceed Anyway'.\");\n\t\t\tif (dontShowAgain.isSelected()) {\n\t\t\t\tSystem.out.println(\"User chose 'Do not show this again' before exiting.\");\n\t\t\t\tPrefs.putPref(prefName, \"true\");\n\t\t\t}\n\t\t\t// In a real app, you would save the 'shouldHide' state here.\n\t\t\tdialog.dispose(); // Close the dialog\n\t\t});\n\n\t\t// 5. Add buttons to the button panel\n\t\tbuttonPanel.add(exitButton);\n\t\tif (!mustExit)\n\t\t\tbuttonPanel.add(proceedButton);\n\n\t\t// Add all main components to the dialog\n\t\tdialog.add(contentPanel, BorderLayout.CENTER);\n\t\tdialog.add(buttonPanel, BorderLayout.SOUTH);\n\n\t\t// 6. Configure and show the dialog\n\t\tdialog.pack(); // Size the dialog based on its contents\n\t\tdialog.setLocationRelativeTo(ownerFrame); // Center the dialog on the screen (relative to the invisible owner)\n\t\tdialog.setVisible(true);\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/control/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  The control package contains classes involved with startup, shutdown, and communication to non-UI clients. This includes the main Borg class\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/CategoryChooser.java",
    "content": "/*\nThis file is part of BORG.\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\nCopyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.TreeSet;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.InputDialog;\n\n/**\n * UI for choosing which categories to display and which to hide\n */\npublic class CategoryChooser extends View {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The singleton. */\n\tprivate static CategoryChooser singleton = null;\n\n\t/**\n\t * Gets the singleton.\n\t * \n\t * @return the singleton\n\t */\n\tpublic static CategoryChooser getReference() {\n\t\tif (singleton == null || !singleton.isShowing())\n\t\t\tsingleton = new CategoryChooser();\n\t\treturn (singleton);\n\t}\n\n\t/** The apply button */\n\tprivate javax.swing.JButton applyButton;\n\n\tprivate javax.swing.JPanel buttonPanel;\n\n\t/** category check boxes */\n\tprivate ArrayList<JCheckBox> cbs = null;\n\n\tprivate javax.swing.JPanel checkBoxPanel;\n\n\t/** The clear button. */\n\tprivate JButton clearAllButton = null;\n\n\tprivate JMenuItem delcatMI;\n\t/** The dismiss button. */\n\tprivate javax.swing.JButton dismissButton;\n\tprivate JScrollPane jScrollPane = null;\n\t/** The select all button */\n\tprivate JButton selectAllButton = null;\n\n\tprivate JPanel topPanel = null;\n\n\t/**\n\t *constructor\n\t */\n\tprivate CategoryChooser() {\n\n\t\tinitComponents();\n\n\t\ttry {\n\t\t\tCategoryModel catmod = CategoryModel.getReference();\n\t\t\tCollection<String> curcats = catmod.getShownCategories();\n\t\t\tCollection<String> allcats = catmod.getCategories();\n\n\t\t\tif (allcats == null) {\n\t\t\t\tallcats = new TreeSet<String>();\n\t\t\t}\n\n\t\t\t// add check boxes for every category\n\t\t\tcbs = new ArrayList<JCheckBox>();\n\n\t\t\tIterator<String> it = allcats.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tString cat = it.next();\n\t\t\t\tJCheckBox cb = new JCheckBox(cat);\n\t\t\t\tcbs.add(cb);\n\n\t\t\t\t// set all currently shown categories to be selected\n\t\t\t\tif (curcats != null && curcats.contains(cat))\n\t\t\t\t\tcb.setSelected(true);\n\n\t\t\t\tcheckBoxPanel.add(cb);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\tpack();\n\t}\n\n\t/**\n\t * apply button action performed.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void applyButtonActionPerformed(java.awt.event.ActionEvent evt) {\n\t\t// set categories\n\t\tTreeSet<String> newcats = new TreeSet<String>();\n\t\tIterator<JCheckBox> it = cbs.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tJCheckBox cb = it.next();\n\t\t\tif (cb.isSelected()) {\n\t\t\t\tnewcats.add(cb.getText());\n\t\t\t}\n\t\t}\n\n\t\t// set the shown categories in the model\n\t\tCategoryModel.getReference().setShownCategories(newcats);\n\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * dismiss button action performed.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void dismissButtonActionPerformed(java.awt.event.ActionEvent evt) {\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * Exit the window.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void exitForm(java.awt.event.WindowEvent evt) {// GEN-FIRST:event_exitForm\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * builds and returns the Category menu items\n\t * \n\t * @return the Category JMenu\n\t */\n\tpublic JMenu getCategoryMenu() {\n\t\t/*\n\t\t * \n\t\t * Categories Menu\n\t\t */\n\t\tJMenu catmenu = new JMenu();\n\n\t\t//catmenu.setIcon(IconHelper.getIcon(\n\t\t//\t\t\"/resource/Preferences16.gif\")));\n\t\tResourceHelper.setText(catmenu, \"Categories\");\n\n\t\tJMenuItem chooseCategoriesMI = new JMenuItem();\n\t\tchooseCategoriesMI.setIcon(IconHelper.getIcon(\"/resource/Preferences16.gif\"));\n\t\tResourceHelper.setText(chooseCategoriesMI, \"choosecat\");\n\t\tchooseCategoriesMI.setActionCommand(\"Choose Displayed Categories\");\n\t\tchooseCategoriesMI\n\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tCategoryChooser.getReference().setVisible(true);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tcatmenu.add(chooseCategoriesMI);\n\n\t\tJMenuItem addCategoryMI = new JMenuItem();\n\t\taddCategoryMI.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Add16.gif\"));\n\t\tResourceHelper.setText(addCategoryMI, \"addcat\");\n\t\taddCategoryMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tString inputValue = null;\n\t\t\t\twhile(true)\n\t\t\t\t{\n\t\t\t\t\tinputValue = InputDialog.show(Resource\n\t\t\t\t\t\t\t.getResourceString(\"AddCat\"), 100);\n\t\t\t\t\tif(inputValue == null)\n\t\t\t\t\t    return; // cancel case\n\t\t\t\t\telse if(inputValue.trim().isEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"Category_Name_Required\"));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\tCategoryModel.getReference().addCategory(inputValue);\n\t\t\t\t\tCategoryModel.getReference().showCategory(inputValue);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t});\n\t\tcatmenu.add(addCategoryMI);\n\n\t\tJMenuItem removeCategoryMI = new JMenuItem();\n\t\tremoveCategoryMI.setIcon(IconHelper.getIcon(\"/resource/Delete16.gif\"));\n\t\tResourceHelper.setText(removeCategoryMI, \"remcat\");\n\t\tremoveCategoryMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tCategoryModel.getReference().sync();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tcatmenu.add(removeCategoryMI);\n\n\t\tcatmenu.add(getDelcatMI());\n\t\t\n\t\treturn catmenu;\n\n\t}\n\n\t/**\n\t * This method initializes clearAllButton.\n\t * \n\t * @return clearAllButton\n\t */\n\tprivate JButton getClearButton() {\n\t\tif (clearAllButton == null) {\n\t\t\tclearAllButton = new JButton();\n\t\t\tResourceHelper.setText(clearAllButton, \"clear_all\");\n\t\t\tclearAllButton\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tIterator<JCheckBox> it = cbs.iterator();\n\t\t\t\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\t\t\t\t// clear all selected check boxes\n\t\t\t\t\t\t\t\tJCheckBox cb = it.next();\n\t\t\t\t\t\t\t\tcb.setSelected(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t\treturn clearAllButton;\n\t}\n\n\t/**\n\t * delete category menu item\n\t */\n\tprivate JMenuItem getDelcatMI() {\n\t\tif (delcatMI == null) {\n\t\t\tdelcatMI = new JMenuItem();\n\t\t\tResourceHelper.setText(delcatMI, \"delete_cat\");\n\t\t\tdelcatMI.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Delete16.gif\"));\n\t\t\tdelcatMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\t// get category list\n\t\t\t\t\t\tCategoryModel catmod = CategoryModel.getReference();\n\t\t\t\t\t\tCollection<String> allcats = catmod.getCategories();\n\t\t\t\t\t\tallcats.remove(CategoryModel.UNCATEGORIZED);\n\t\t\t\t\t\tif (allcats.isEmpty())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"No_Categories\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tObject[] cats = allcats.toArray();\n\n\t\t\t\t\t\t// ask user to choose a category\n\t\t\t\t\t\tObject o = JOptionPane.showInputDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"delete_cat_choose\"), \"\",\n\t\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE, null, cats,\n\t\t\t\t\t\t\t\tcats[0]);\n\t\t\t\t\t\tif (o == null)\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// confirm with user\n\t\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"delcat_warn\")\n\t\t\t\t\t\t\t\t+ \" [\" + o + \"]!\", \"\",\n\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.WARNING_MESSAGE);\n\t\t\t\t\t\tif (ret == JOptionPane.OK_OPTION) {\n\n\t\t\t\t\t\t\t// deletes all appts and tasks with that cetegory\n\t\t\t\t\t\t\t// !!!!!\n\n\t\t\t\t\t\t\t// appts\n\t\t\t\t\t\t\tIterator<?> itr = AppointmentModel.getReference()\n\t\t\t\t\t\t\t\t\t.getAllAppts().iterator();\n\t\t\t\t\t\t\twhile (itr.hasNext()) {\n\t\t\t\t\t\t\t\tAppointment ap = (Appointment) itr.next();\n\t\t\t\t\t\t\t\tString cat = ap.getCategory();\n\t\t\t\t\t\t\t\tif (cat != null && cat.equals(o))\n\t\t\t\t\t\t\t\t\tAppointmentModel.getReference().delAppt(ap);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// tasks\n\t\t\t\t\t\t\titr = TaskModel.getReference().getTasks()\n\t\t\t\t\t\t\t\t\t.iterator();\n\t\t\t\t\t\t\twhile (itr.hasNext()) {\n\t\t\t\t\t\t\t\tTask t = (Task) itr.next();\n\t\t\t\t\t\t\t\tString cat = t.getCategory();\n\t\t\t\t\t\t\t\tif (cat != null && cat.equals(o))\n\t\t\t\t\t\t\t\t\tTaskModel.getReference().delete(t.getKey());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//projects\n\t\t\t\t\t\t\titr = TaskModel.getReference().getProjects()\n\t\t\t\t\t\t\t\t\t.iterator();\n\t\t\t\t\t\t\twhile (itr.hasNext()) {\n\t\t\t\t\t\t\t\tProject t = (Project) itr.next();\n\t\t\t\t\t\t\t\tString cat = t.getCategory();\n\t\t\t\t\t\t\t\tif (cat != null && cat.equals(o))\n\t\t\t\t\t\t\t\t\tTaskModel.getReference().deleteProject(t.getKey());\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t//CategoryModel.getReference().sync();\n\t\t\t\t\t\t\t\tCategoryModel.getReference().deleteCategory((String)o);\n\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn delcatMI;\n\t}\n\n\t/**\n\t * This method initializes jScrollPane.\n\t * \n\t * @return jScrollPane\n\t */\n\tprivate JScrollPane getJScrollPane() {\n\t\tif (jScrollPane == null) {\n\t\t\tjScrollPane = new JScrollPane();\n\t\t\tjScrollPane.setPreferredSize(new java.awt.Dimension(40, 200));\n\t\t\tjScrollPane.setViewportView(checkBoxPanel);\n\t\t}\n\t\treturn jScrollPane;\n\t}\n\n\t/**\n\t * This method initializes topPanel.\n\t * \n\t * @return topPanel\n\t */\n\tprivate JPanel getTopPanel() {\n\t\tif (topPanel == null) {\n\t\t\ttopPanel = new JPanel();\n\t\t\ttopPanel.setLayout(new GridBagLayout());\n\t\t\ttopPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\t\ttopPanel.add(getJScrollPane(), GridBagConstraintsFactory.create(0,\n\t\t\t\t\t0, GridBagConstraints.BOTH, 1.0, 1.0));\n\t\t}\n\t\treturn topPanel;\n\t}\n\n\t/**\n\t * initialize the UI components\n\t */\n\tprivate void initComponents() {\n\n\t\tGridLayout gridLayout2 = new GridLayout();\n\t\tcheckBoxPanel = new javax.swing.JPanel();\n\t\tbuttonPanel = new javax.swing.JPanel();\n\t\tapplyButton = new javax.swing.JButton();\n\t\tdismissButton = new javax.swing.JButton();\n\n\t\tgetContentPane().setLayout(new java.awt.GridBagLayout());\n\n\t\tsetDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);\n\t\tthis.setTitle(Resource.getResourceString(\"catchooser\"));\n\t\taddWindowListener(new java.awt.event.WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(java.awt.event.WindowEvent evt) {\n\t\t\t\texitForm(evt);\n\t\t\t}\n\t\t});\n\n\t\tcheckBoxPanel.setLayout(new java.awt.GridLayout(0, 1));\n\n\t\tcheckBoxPanel.setBorder(new javax.swing.border.LineBorder(\n\t\t\t\tnew java.awt.Color(0, 0, 0), 1, true));\n\t\tbuttonPanel.setLayout(gridLayout2);\n\n\t\tgetContentPane().add(\n\t\t\t\tcheckBoxPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH,\n\t\t\t\t\t\t1.0, 1.0));\n\n\t\tapplyButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Save16.gif\"));\n\t\tResourceHelper.setText(applyButton, \"apply\");\n\t\tapplyButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tapplyButtonActionPerformed(evt);\n\t\t\t}\n\t\t});\n\n\t\tdismissButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Stop16.gif\"));\n\t\tResourceHelper.setText(dismissButton, \"Dismiss\");\n\t\tdismissButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tdismissButtonActionPerformed(evt);\n\t\t\t}\n\t\t});\n\t\tsetDismissButton(dismissButton);\n\n\t\tthis.setSize(250, 147);\n\t\tthis.setContentPane(getTopPanel());\n\t\tgridLayout2.setRows(1);\n\t\tbuttonPanel.add(selectAllButton(), null);\n\t\tbuttonPanel.add(getClearButton(), null);\n\t\tbuttonPanel.add(applyButton, null);\n\t\tbuttonPanel.add(dismissButton, null);\n\t\tgetContentPane().add(\n\t\t\t\tbuttonPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\t\tGridBagConstraints.HORIZONTAL));\n\n\t\tpack();\n\t}\n\t@Override\n\tpublic void refresh() {\n\t  // empty\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\t\n\t/**\n\t * This method initializes selectAllButton.\n\t * \n\t * @return selectAllButton\n\t */\n\tprivate JButton selectAllButton() {\n\t\tif (selectAllButton == null) {\n\t\t\tselectAllButton = new JButton();\n\t\t\tResourceHelper.setText(selectAllButton, \"select_all\");\n\t\t\tselectAllButton\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tIterator<JCheckBox> it = cbs.iterator();\n\t\t\t\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\t\t\t\t// select all check boxes\n\t\t\t\t\t\t\t\tJCheckBox cb = it.next();\n\t\t\t\t\t\t\t\tcb.setSelected(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t\treturn selectAllButton;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/ClipBoard.java",
    "content": "package net.sf.borg.ui;\n\nimport java.util.HashMap;\n\n/**\n * a place for the UI to copy items for later pasting\n */\npublic class ClipBoard {\n\t\n\tprivate static final ClipBoard singleton = new ClipBoard();\n\t\n\tprivate final HashMap<Class<?>, Object> map = new HashMap<Class<?>, Object>();\n\t\n\tpublic static ClipBoard getReference() {\n\t\treturn singleton;\n\t}\n\t\n\tpublic void put( Object o )\n\t{\n\t\tmap.put(o.getClass(), o);\n\t}\n\t\n\tpublic Object get( Class<?> c)\n\t{\n\t\treturn map.get(c);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/DockableView.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui;\n\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Image;\nimport java.awt.Toolkit;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.swing.JComponent;\nimport javax.swing.JFrame;\nimport javax.swing.JPanel;\nimport javax.swing.KeyStroke;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.ViewSize.DockType;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * The Class DockableView is the base class for panels that can appear as\n * stand-alone windows or tabs in the main view and can be docked/undocked at\n * runtime.\n */\npublic abstract class DockableView extends JPanel implements Model.Listener {\n\n\t/** The icon for the title bar. */\n\tstatic Image image = Toolkit.getDefaultToolkit()\n\t\t\t.getImage(DockableView.class.getResource(\"/resource/borg32x32.jpg\"));\n\n\t/** The Constant serialVersionUID. */\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** main menu bar */\n\tprivate MainMenu mainMenu = null;\n\n\tprivate final Set<Model> models = new HashSet<Model>();\n\n\n\t/** The main frame. */\n\tprivate JFrame frame = null;\n\n\t/**\n\t * register the view for model change callbacks.\n\t * \n\t * @param m the model\n\t */\n\tprotected void addModel(Model m) {\n\t\tm.addListener(this);\n\t\tmodels.add(m);\n\t}\n\n\t/**\n\t * method called to check if the view can be closed.\n\t * \n\t * @return true if the view can be closed, false if not\n\t */\n\tprotected boolean canClose() {\n\t\t// default is to always allow close\n\t\treturn true;\n\t}\n\n\t/**\n\t * method called when view is being closed to allow the view to do clean up or\n\t * resets.\n\t */\n\tprotected void cleanUp() {\n\n\t\t// modules persist when closed, so don't deregister the listeners\n\t\tif (this instanceof MultiView.Module)\n\t\t\treturn;\n\n\t\tfor (Model m : models)\n\t\t\tm.removeListener(this);\n\t}\n\n\t/**\n\t * close the view if allowed.\n\t */\n\tpublic void close() {\n\n\t\tif (!canClose())\n\t\t\treturn;\n\n\t\tcleanUp();\n\n\t\tif (isDocked())\n\t\t\tthis.getParent().remove(this);\n\t\telse {\n\t\t\tframe.dispose();\n\t\t\tframe = null;\n\t\t}\n\t}\n\n\t/**\n\t * Dock into the multiview.\n\t */\n\tprivate void dock() {\n\t\tMultiView.getMainView().addView(getFrameTitle(), this);\n\t\tif (frame != null)\n\t\t\tframe.dispose();\n\t\tframe = null;\n\t\tString s = Prefs.getPref(getFrameSizePref());\n\t\tViewSize vs = ViewSize.fromString(s);\n\t\tvs.setDock(DockType.DOCK);\n\t\tPrefs.putPref(getFrameSizePref(), vs.toString());\n\t}\n\n\tprotected void updateTitle() {\n\t\tif (isDocked()) {\n\t\t\tMultiView.getMainView().setTabTitle(getFrameTitle(), this);\n\t\t} else if (frame != null) {\n\t\t\tframe.setTitle(getFrameTitle());\n\t\t}\n\t}\n\n\t/**\n\t * Gets the window size preference.\n\t * \n\t * @return the window size preference\n\t */\n\tprotected PrefName getFrameSizePref() {\n\t\treturn new PrefName(getFrameTitle() + \"_framesize\", \"-1,-1,800,600,N\");\n\t}\n\n\t/**\n\t * Gets the frame title.\n\t * \n\t * @return the frame title\n\t */\n\tpublic abstract String getFrameTitle();\n\n\t/**\n\t * determine if the view is docked.\n\t * \n\t * @return true if docked\n\t */\n\tpublic boolean isDocked() {\n\t\treturn frame == null;\n\t}\n\n\n\t/**\n\t * Open the view in a frame.\n\t * \n\t * @return the frame\n\t */\n\tpublic JFrame openInFrame() {\n\t\tframe = new JFrame();\n\t\tViewSize.manageMySize(frame, getFrameSizePref());\n\t\tframe.setContentPane(this);\n\n\t\tif (mainMenu == null) {\n\t\t\tmainMenu = new MainMenu();\n\n\t\t\t/* add a print option if available */\n\t\t\tif (this instanceof Module) {\n\t\t\t\tfinal Module mod = (Module) this;\n\t\t\t\tmainMenu.addAction(IconHelper.getIcon(\"/resource/Print16.gif\"),\n\t\t\t\t\t\tResource.getResourceString(\"Print\"), new ActionListener() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\t\t\tmod.print();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 0);\n\t\t\t}\n\n\t\t\t// add a dock menu option\n\t\t\tmainMenu.addAction(null, Resource.getResourceString(\"dock\"), new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\tdock();\n\t\t\t\t}\n\t\t\t}, 0);\n\t\t}\n\n\t\tframe.setJMenuBar(mainMenu.getMenuBar());\n\n\t\tframe.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);\n\t\tframe.setTitle(getFrameTitle());\n\n\t\tfinal DockableView dv = this;\n\t\tframe.addWindowListener(new java.awt.event.WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(java.awt.event.WindowEvent evt) {\n\t\t\t\tdv.close();\n\t\t\t}\n\t\t});\n\n\t\tframe.setIconImage(image);\n\t\tframe.getLayeredPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\tdv.close();\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);\n\t\tframe.setVisible(true);\n\n\t\treturn frame;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.Model.Listener#refresh()\n\t */\n\t/**\n\t * Refresh.\n\t */\n\tpublic abstract void refresh();\n\n\t/**\n\t * Shows the view as a docked tab or separate window, depending on the user\n\t * options.\n\t * \n\t */\n\tpublic void showView() {\n\n\t\t// if showing already....\n\t\tif (frame != null && frame.isDisplayable()) {\n\t\t\tframe.toFront();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.isDisplayable()) {\n\t\t\t// already showing\n\t\t\treturn;\n\t\t}\n\n\t\t// check if dock/undock is overridden for this window\n\t\t// if so, use the override instead of the system preference\n\t\tPrefName p = getFrameSizePref();\n\t\tViewSize vs = ViewSize.fromString(Prefs.getPref(p));\n\t\tif (vs.getDock() == DockType.DOCK) {\n\t\t\tthis.dock();\n\t\t} else {\n\t\t\tthis.openInFrame();\n\t\t}\n\t}\n\n\t/**\n\t * start this view in the background (dock-only) - when starting to system tray\n\t */\n\tpublic void bgStart() {\n\t\tPrefName p = getFrameSizePref();\n\t\tViewSize vs = ViewSize.fromString(Prefs.getPref(p));\n\t\tif (vs.getDock() == DockType.DOCK) {\n\t\t\tthis.dock();\n\t\t}\n\t}\n\n\tpublic static DockableView findDockableParent(Component c) {\n\t\tfor (Container cont = c.getParent(); cont != null; cont = cont.getParent()) {\n\t\t\tif (cont instanceof DockableView) {\n\t\t\t\treturn (DockableView) cont;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/EntitySelector.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport javax.swing.DefaultListSelectionModel;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.TableModel;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.MemoModel;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.Memo;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * displays dialogs to let the user select various borg entities\n */\npublic class EntitySelector extends JDialog {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The used to hold the chosen entities */\n\tprivate static final ArrayList<Object> list_ = new ArrayList<Object>();\n\n\t/**\n\t * Prompt the user to select an address.\n\t * \n\t * @return the address\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic static Address selectAddress() throws Exception {\n\t\tCollection<Address> addrs = AddressModel.getReference().getAddresses();\n\t\treturn ((Address) EntitySelector\n\t\t\t\t.selectBean(addrs,\n\t\t\t\t\t\tnew TableSorter(\n\t\t\t\t\t\t\t\tnew String[] { Resource.getResourceString(\"Last\"),\n\t\t\t\t\t\t\t\t\t\tResource.getResourceString(\"First\") },\n\t\t\t\t\t\t\t\tnew Class[] { String.class, String.class }),\n\t\t\t\t\t\tnew String[] { \"LastName\", \"FirstName\" }));\n\t}\n\n\t/**\n\t * Prompt the user to select an appointment.\n\t * \n\t * @return the appointment\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic static Appointment selectAppointment(List<Appointment> exclude) throws Exception {\n\n\t\tCollection<Appointment> apps = AppointmentModel.getReference().getAllAppts();\n\t\tapps.removeAll(exclude);\n\t\tif (Prefs.getPref(PrefName.SHOWPRIVATE).equals(\"false\")) {\n\t\t\tCollection<Appointment> privAppts = new LinkedList<Appointment>();\n\t\t\tfor (Appointment app : apps) {\n\t\t\t\tif (app.isPrivate())\n\t\t\t\t\tprivAppts.add(app);\n\t\t\t}\n\t\t\tapps.removeAll(privAppts);\n\t\t}\n\t\tObject ap = selectBean(apps,\n\t\t\t\tnew TableSorter(new String[] { Resource.getResourceString(\"Text\"), Resource.getResourceString(\"Time\") },\n\t\t\t\t\t\tnew Class[] { String.class, Date.class }),\n\t\t\t\tnew String[] { \"ClearText\", \"Date\" });\n\t\treturn (Appointment)ap;\n\n\t}\n\n\t/**\n\t * Prompt the user to select an entity.\n\t * \n\t * @param records\n\t *            the list of entities\n\t * @param tm\n\t *            the table model to insert the entities into\n\t * @param fields\n\t *            the array of fields to show in the table\n\t * \n\t * @return the object\n\t */\n\tprivate static Object selectBean(Collection<? extends Object> records, TableModel tm, String[] fields) {\n\t\tnew EntitySelector(records, tm, fields, false).setVisible(true);\n\t\tif (list_.size() != 0) {\n\t\t\tObject b = list_.get(0);\n\t\t\treturn b;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Prompt the user to select a memo.\n\t * \n\t * @return the memo\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic static Memo selectMemo() throws Exception {\n\n\t\tCollection<Memo> memos = MemoModel.getReference().getMemos();\n\t\treturn ((Memo) selectBean(memos,\n\t\t\t\tnew TableSorter(new String[] { Resource.getResourceString(\"Memo_Name\") }, new Class[] { String.class }),\n\t\t\t\tnew String[] { \"MemoName\" }));\n\t}\n\n\t/**\n\t * Prompt the user to select a project.\n\t * \n\t * @return the project\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic static Project selectProject() throws Exception {\n\t\tCollection<Project> projects = TaskModel.getReference().getProjects();\n\t\treturn ((Project) EntitySelector.selectBean(projects,\n\t\t\t\tnew TableSorter(\n\t\t\t\t\t\tnew String[] { Resource.getResourceString(\"Item_#\"),\n\t\t\t\t\t\t\t\tResource.getResourceString(\"Description\") },\n\t\t\t\t\t\tnew Class[] { Integer.class, String.class }),\n\t\t\t\tnew String[] { \"Key\", \"Description\" }));\n\t}\n\n\t/**\n\t * Prompt the user to select a task.\n\t * \n\t * @return the task\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic static Task selectTask() throws Exception {\n\t\tCollection<Task> tasks = TaskModel.getReference().getTasks();\n\t\treturn ((Task) EntitySelector.selectBean(tasks,\n\t\t\t\tnew TableSorter(\n\t\t\t\t\t\tnew String[] { Resource.getResourceString(\"Item_#\"),\n\t\t\t\t\t\t\t\tResource.getResourceString(\"Description\") },\n\t\t\t\t\t\tnew Class[] { Integer.class, String.class }),\n\t\t\t\tnew String[] { \"Key\", \"Description\" }));\n\t}\n\n\tprivate JButton clearButton;\n\n\t/** The names of the entity fields to show in the table */\n\tprivate final String[] fields_;\n\n\tprivate javax.swing.JPanel jPanel1;\n\n\tprivate javax.swing.JScrollPane jScrollPane1;\n\n\tprivate javax.swing.JTable jTable1;\n\n\t/** The records_. */\n\tprivate Collection<? extends Object> records_ = null;\n\n\tprivate javax.swing.JButton selectButton;\n\n\t/**\n\t * constructor\n\t * \n\t * @param records\n\t *            the list of entities\n\t * @param tm\n\t *            the table model to insert the entities into\n\t * @param fields\n\t *            the array of fields to show in the table\n\t * \n\t * @param multiple\n\t *            if true, allow multiple entities to be selected\n\t */\n\tprivate EntitySelector(Collection<? extends Object> records, TableModel tm, String[] fields, boolean multiple) {\n\n\t\tsuper();\n\t\tsetModal(true);\n\t\trecords_ = records;\n\n\t\tfields_ = fields;\n\t\tlist_.clear();\n\n\t\t// init the gui components\n\t\tinitComponents();\n\n\t\tif (tm != null) {\n\t\t\tjTable1.setModel(tm);\n\t\t}\n\n\t\tthis.setTitle(Resource.getResourceString(\"Select\"));\n\n\t\tif (multiple)\n\t\t\tjTable1.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\t\telse\n\t\t\tjTable1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tjTable1.addMouseListener(new java.awt.event.MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(java.awt.event.MouseEvent evt) {\n\t\t\t\tjTable1MouseClicked(evt);\n\t\t\t}\n\t\t});\n\n\t\tloadData();\n\n\t\tpack();\n\n\t}\n\n\t/**\n\t * Clearbutton action performed.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void clearbuttonActionPerformed(java.awt.event.ActionEvent evt) {\n\t\tlist_.clear();\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * Initialize the UI components\n\t */\n\tprivate void initComponents() {\n\n\t\tthis.getContentPane().setLayout(new GridBagLayout());\n\t\tjTable1 = new JTable();\n\t\tjPanel1 = new javax.swing.JPanel();\n\t\tselectButton = new javax.swing.JButton();\n\n\t\tjTable1.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)));\n\n\t\tDefaultListSelectionModel mylsmodel = new DefaultListSelectionModel();\n\t\tmylsmodel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\t\tjTable1.setSelectionModel(mylsmodel);\n\t\tjTable1.addMouseListener(new java.awt.event.MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(java.awt.event.MouseEvent evt) {\n\t\t\t\tjTable1MouseClicked(evt);\n\t\t\t}\n\t\t});\n\n\t\tjScrollPane1 = new JScrollPane();\n\t\tjScrollPane1.setPreferredSize(new java.awt.Dimension(554, 404));\n\t\tjScrollPane1.setViewportView(jTable1);\n\t\tjTable1.setShowGrid(true);\n\t\tjTable1.setIntercellSpacing(new Dimension(1, 1));\n\n\n\t\tselectButton.setIcon(IconHelper.getIcon(\"/resource/Add16.gif\"));\n\t\tselectButton.setText(Resource.getResourceString(\"Select\"));\n\n\t\tselectButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tselectbuttonActionPerformed(evt);\n\t\t\t}\n\t\t});\n\n\t\tjPanel1.add(selectButton);\n\n\t\tclearButton = new JButton();\n\t\tclearButton.setIcon(IconHelper.getIcon(\"/resource/Undo16.gif\"));\n\t\tclearButton.setText(Resource.getResourceString(\"Clear\"));\n\t\tclearButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tclearbuttonActionPerformed(evt);\n\t\t\t}\n\t\t});\n\n\t\tjPanel1.add(clearButton);\n\n\t\tthis.getContentPane().add(jScrollPane1,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\tthis.getContentPane().add(jPanel1, GridBagConstraintsFactory.create(0, 2));\n\n\t}\n\n\t/**\n\t * table mouse clicked.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void jTable1MouseClicked(java.awt.event.MouseEvent evt) {\n\t\tif (evt.getClickCount() > 1) {\n\t\t\tselectbuttonActionPerformed(null);\n\t\t}\n\t}\n\n\t/**\n\t * Load the entities from the database into the table.\n\t */\n\tpublic void loadData() {\n\n\t\t// init the table to empty\n\t\tTableSorter tm = (TableSorter) jTable1.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(jTable1);\n\t\ttm.setRowCount(0);\n\n\t\tIterator<? extends Object> it = records_.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tObject r = it.next();\n\n\t\t\ttry {\n\t\t\t\tClass<? extends Object> beanClass = r.getClass();\n\t\t\t\tObject[] ro = new Object[fields_.length];\n\t\t\t\tfor (int i = 0; i < fields_.length; i++) {\n\t\t\t\t\t// find method\n\t\t\t\t\tString method = \"get\" + fields_[i];\n\t\t\t\t\tMethod m = beanClass.getMethod(method, (Class[]) null);\n\t\t\t\t\tro[i] = m.invoke(r, (Object[]) null);\n\t\t\t\t}\n\t\t\t\t// add the table row\n\n\t\t\t\ttm.addRow(ro);\n\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Selectbutton action performed.\n\t * \n\t * @param evt\n\t *            the evt\n\t */\n\tprivate void selectbuttonActionPerformed(java.awt.event.ActionEvent evt) {\n\t\t// figure out which row is selected.\n\t\tlist_.clear();\n\t\tint[] index = jTable1.getSelectedRows();\n\t\tif (index.length == 0)\n\t\t\treturn;\n\n\t\tfor (int i = 0; i < index.length; i++) {\n\t\t\ttry {\n\t\t\t\t// need to ask the table for the original (befor sorting) index\n\t\t\t\t// of\n\t\t\t\t// the selected row\n\t\t\t\tTableSorter tm = (TableSorter) jTable1.getModel();\n\t\t\t\tint k = tm.getMappedIndex(index[i]); // get original index -\n\t\t\t\t// not\n\t\t\t\t// current sorted position in\n\t\t\t\t// tbl\n\t\t\t\tObject[] oa = records_.toArray();\n\t\t\t\tlist_.add(oa[k]);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t\tthis.dispose();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/FileView.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n/*\n * helpscrn.java\n *\n * Created on October 5, 2003, 8:55 AM\n */\n\npackage net.sf.borg.ui;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport javax.swing.text.Document;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.print.PrinterException;\nimport java.io.IOException;\n\n/**\n * dockable view that shows information from a file\n */\nclass FileView extends DockableView implements Module {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate JEditorPane jEditorPane;\n\tprivate JScrollPane jScrollPane1;\n\n\t/** window title */\n\tprivate final String title_;\n\tprivate final String file_;\n\t\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t * @param file file to read text or html from\n\t * @param title window title\n\t */\n\tFileView(String file, String title) {\n\t\ttitle_ = title;\n\t\tfile_ = file;\n\t}\n\t\n\t@Override\n\tpublic void showView()\n\t{\n\t\tif( !isInitialized)\n\t\t{\n\t\t\tinitComponents();\n\t\t\ttry {\n\t\t\t\tjEditorPane.setPage(\"file:///\" + file_);\n\t\t\t} catch (java.io.IOException e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\t\t\tisInitialized = true;\n\t\t}\n\t\t\n\t\tsuper.showView();\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn title_;\n\t}\n\n\t/**\n\t * initialize the UI\n\t */\n\tprivate void initComponents()\n\t{\n\t\tsetLayout(new GridBagLayout());\n\t\tjScrollPane1 = new javax.swing.JScrollPane();\n\t\tjEditorPane = new javax.swing.JEditorPane();\n\t\tjEditorPane.setEditable(false);\n\t\tjScrollPane1.setViewportView(jEditorPane);\n\t\tadd(jScrollPane1, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\tJPanel bPanel = new JPanel();\n\t\tJButton reloadButton = new JButton(Resource.getResourceString(\"reload\"));\n\t\treloadButton.addActionListener(new ActionListener(){\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tDocument doc = jEditorPane.getDocument();\n\t\t\t\tdoc.putProperty(Document.StreamDescriptionProperty, null);\n\t\t\t\ttry {\n\t\t\t\t\tjEditorPane.setPage(\"file:///\" + file_);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t});\n\t\tbPanel.add(reloadButton);\n\t\tadd(bPanel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t  // empty\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\t\n\t@Override\n\tpublic void print()\n\t{\n\t\ttry {\n\t\t\tjEditorPane.print();\n\t\t} catch (PrinterException e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic JComponent getComponent() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn title_;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tparent.addHelpMenuItem(null, getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tshowView();\n\t\t\t}\n\t\t});\n\t\t\n\t}\n\t\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn null;\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/HelpLauncher.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport javax.help.HelpBroker;\nimport javax.help.HelpSet;\nimport java.net.URL;\n\n/**\n * launches the borg java help\n *\n */\npublic class HelpLauncher {\n\n\t/**\n\t * launch the Borg java help\n\t */\n\tpublic static void launchHelp() throws Exception {\n\t\t// Find the HelpSet file and create the HelpSet object:\n\t\tString helpHS = \"BorgHelp.hs\";\n\t\tClassLoader cl = HelpLauncher.class.getClassLoader();\n\t\tURL hsURL = HelpSet.findHelpSet(cl, helpHS);\n\t\tHelpSet hs = new HelpSet(null, hsURL);\n\t\tHelpBroker hb = hs.createHelpBroker();\n\t\thb.initPresentation();\n\t\thb.setDisplayed(true);\n\t}\n\n\tstatic public void main(String[] args) throws Exception {\n\t\tlaunchHelp();\n\t}\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/InfoView.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n/*\n * helpscrn.java\n *\n * Created on October 5, 2003, 8:55 AM\n */\n\npackage net.sf.borg.ui;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.print.PrinterException;\n\n/**\n * dockable view that shows informational text or html to the user in a\n * scrollable pane\n */\nclass InfoView extends DockableView implements Module {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate JEditorPane jEditorPane1;\n\tprivate JScrollPane jScrollPane1;\n\n\t/** window title */\n\tprivate final String title_;\n\tprivate final String file_;\n\t\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t * @param file file to read text or html from\n\t * @param title window title\n\t */\n\tInfoView(String file, String title) {\n\t\ttitle_ = title;\n\t\tfile_ = file;\n\t}\n\t\n\t@Override\n\tpublic void showView()\n\t{\n\t\tif( !isInitialized)\n\t\t{\n\t\t\tinitComponents();\n\t\t\ttry {\n\t\t\t\tjEditorPane1.setPage(getClass().getResource(file_));\n\t\t\t} catch (java.io.IOException e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\t\t\tisInitialized = true;\n\t\t}\n\t\t\n\t\tsuper.showView();\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn title_;\n\t}\n\n\t/**\n\t * initialize the UI\n\t */\n\tprivate void initComponents()\n\t{\n\t\tsetLayout(new GridBagLayout());\n\t\tjScrollPane1 = new javax.swing.JScrollPane();\n\t\tjEditorPane1 = new javax.swing.JEditorPane();\n\t\tjEditorPane1.setEditable(false);\n\t\tjScrollPane1.setViewportView(jEditorPane1);\n\t\tadd(jScrollPane1, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t  // empty\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\t\n\t@Override\n\tpublic void print()\n\t{\n\t\ttry {\n\t\t\tjEditorPane1.print();\n\t\t} catch (PrinterException e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic JComponent getComponent() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn title_;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tparent.addHelpMenuItem(null, getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tshowView();\n\t\t\t}\n\t\t});\n\t\t\n\t}\n\t\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn null;\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/MainMenu.java",
    "content": "/*\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2006 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileReader;\nimport java.io.InputStream;\nimport java.util.Properties;\n\nimport javax.swing.Box;\nimport javax.swing.Icon;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JFileChooser;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuBar;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.SwingUtilities;\nimport javax.swing.event.MenuEvent;\nimport javax.swing.event.MenuListener;\nimport javax.swing.filechooser.FileNameExtensionFilter;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.IOHelper;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.EmailReminder;\nimport net.sf.borg.model.ExportImport;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.model.undo.UndoLog;\nimport net.sf.borg.ui.options.OptionsView;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PasswordHelper;\nimport net.sf.borg.ui.util.ScrolledDialog;\n\n/**\n * The borg main menu bar\n * \n */\nclass MainMenu {\n\n\tprivate final JMenu actionMenu = new JMenu();\n\tprivate final JMenu helpmenu = new JMenu();\n\tprivate final JMenu optionsMenu = new JMenu();\n\tprivate final JMenu viewMenu = new JMenu();\n\tprivate JMenu pluginMenu = null;\n\tprivate final JMenuBar menuBar = new JMenuBar();\n\n\t/**\n\t * constructor\n\t */\n\tpublic MainMenu() {\n\n\t\tmenuBar.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED));\n\t\t// actionMenu.setIcon(IconHelper.getIcon(\n\t\t// \"/resource/Application16.gif\")));\n\n\t\t/*\n\t\t * \n\t\t * Action Menu - will contain static items below and other actions inserted by\n\t\t * UI Modules UI Module actions will be inserted above the items below\n\t\t */\n\t\tResourceHelper.setText(actionMenu, \"Action\");\n\n\t\tJMenuItem sqlMI = new JMenuItem();\n\t\tsqlMI.setIcon(IconHelper.getIcon(\"/resource/Refresh16.gif\"));\n\t\tResourceHelper.setText(sqlMI, \"RunSQL\");\n\t\tsqlMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tnew SqlRunner().setVisible(true);\n\t\t\t}\n\t\t});\n\t\tactionMenu.add(sqlMI);\n\n\t\tJMenuItem resendEmailMI = new JMenuItem();\n\t\t// resendEmailMI.setIcon(IconHelper.getIcon(\n\t\t// \"/resource/Refresh16.gif\")));\n\t\tResourceHelper.setText(resendEmailMI, \"resendEmail\");\n\t\tresendEmailMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tString email = Prefs.getPref(PrefName.EMAILENABLED);\n\t\t\t\t\tif (email.equals(\"false\")) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"emailNotEnabled\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tString pass = PasswordHelper.getReference().decryptText( Prefs.getPref(PrefName.EMAILPASS), \"Unlock Email Password\", false);\n\t\t\t\t\tif( pass == null ) return;\n\t\t\t\t\tclass MailThread extends Thread {\n\t\t\t\t\t\tprivate String passwd;\n\t\t\t\t\t\tpublic MailThread(String pass) {\n\t\t\t\t\t\t\tthis.passwd = pass;\n\t\t\t\t\t\t\tthis.setName(\"Reminder Mail Thread\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tEmailReminder.sendDailyEmailReminder(null, true, passwd);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tfinal Exception fe = e;\n\t\t\t\t\t\t\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(fe);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tnew MailThread(pass).start();\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tactionMenu.add(resendEmailMI);\n\n\t\tJMenuItem exitMenuItem = new JMenuItem();\n\t\texitMenuItem.setIcon(IconHelper.getIcon(\"/resource/stop.png\"));\n\t\texitMenuItem.setText(Resource.getResourceString(\"Exit\"));\n\t\texitMenuItem.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tUIControl.shutDownUI();\n\t\t\t}\n\t\t});\n\t\tactionMenu.add(exitMenuItem);\n\n\t\tmenuBar.add(actionMenu);\n\n\t\t/*\n\t\t * \n\t\t * Option Menu\n\t\t */\n\n\t\t// optionsMenu.setIcon(IconHelper.getIcon(\n\t\t// \"/resource/Preferences16.gif\")));\n\t\tResourceHelper.setText(optionsMenu, \"Options\");\n\n\t\tJMenuItem editPrefsMenuItem = new JMenuItem();\n\t\tResourceHelper.setText(editPrefsMenuItem, \"ep\");\n\t\teditPrefsMenuItem.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tOptionsView.getReference().setVisible(true);\n\t\t\t}\n\t\t});\n\t\toptionsMenu.add(editPrefsMenuItem);\n\n\t\tJMenuItem exportPrefsMI = new JMenuItem();\n\t\texportPrefsMI.setIcon(IconHelper.getIcon(\"/resource/Export16.gif\"));\n\t\tResourceHelper.setText(exportPrefsMI, \"export_prefs\");\n\t\texportPrefsMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\texpPrefs();\n\t\t\t}\n\t\t});\n\t\toptionsMenu.add(exportPrefsMI);\n\n\t\tJMenuItem importPrefsMI = new JMenuItem();\n\t\timportPrefsMI.setIcon(IconHelper.getIcon(\"/resource/Import16.gif\"));\n\t\tResourceHelper.setText(importPrefsMI, \"import_prefs\");\n\t\timportPrefsMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\timpPrefs();\n\t\t\t}\n\t\t});\n\t\toptionsMenu.add(importPrefsMI);\n\n\t\tmenuBar.add(optionsMenu);\n\n\t\tResourceHelper.setText(viewMenu, \"View\");\n\t\t\n\t\tJCheckBoxMenuItem privateMI = new JCheckBoxMenuItem();\n\t\tprivateMI.setText(Resource.getResourceString(\"Show_Private_Appointments\"));\n\t\tprivateMI.putClientProperty(\"CheckBoxMenuItem.doNotCloseOnMouseClick\", true);\n\t\tprivateMI.setSelected(Prefs.getBoolPref(PrefName.SHOWPRIVATE));\n\t\tprivateMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tPrefs.putPref(PrefName.SHOWPRIVATE, privateMI.isSelected() ? \"true\" : \"false\");\n\t\t\t\tAppointmentModel.getReference().refresh();\n\t\t\t}\n\t\t});\n\t\tviewMenu.add(privateMI);\n\t\t\n\t\tJCheckBoxMenuItem subCalMI = new JCheckBoxMenuItem();\n\t\tsubCalMI.setText(Resource.getResourceString(\"subscribed_cals\"));\n\t\tsubCalMI.putClientProperty(\"CheckBoxMenuItem.doNotCloseOnMouseClick\", true);\n\t\tsubCalMI.setSelected(true);\n\t\tsubCalMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tSubscribedCalendars.getReference().setShow(subCalMI.isSelected());\n\n\t\t\t}\n\t\t});\n\t\tviewMenu.add(subCalMI);\n\t\t\n\t\tJCheckBoxMenuItem futureTodoMI = new JCheckBoxMenuItem();\n\t\tfutureTodoMI.setText(Resource.getResourceString(\"future_todo_rpts\"));\n\t\tfutureTodoMI.putClientProperty(\"CheckBoxMenuItem.doNotCloseOnMouseClick\", true);\n\t\tfutureTodoMI.setSelected(!Prefs.getBoolPref(PrefName.TODO_ONLY_SHOW_CURRENT));\n\t\tfutureTodoMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tPrefs.putPref(PrefName.TODO_ONLY_SHOW_CURRENT, futureTodoMI.isSelected() ? \"false\" : \"true\");\n\t\t\t\tAppointmentModel.getReference().refresh();\n\t\t\t}\n\t\t});\n\t\tviewMenu.add(futureTodoMI);\n\t\t\n\t\tJCheckBoxMenuItem strikeMI = new JCheckBoxMenuItem();\n\t\tstrikeMI.setText(Resource.getResourceString(\"hide_strike\"));\n\t\tstrikeMI.putClientProperty(\"CheckBoxMenuItem.doNotCloseOnMouseClick\", true);\n\t\tstrikeMI.setSelected(!Prefs.getBoolPref(PrefName.HIDESTRIKETHROUGH));\n\t\tstrikeMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tPrefs.putPref(PrefName.HIDESTRIKETHROUGH, strikeMI.isSelected() ? \"false\" : \"true\");\n\t\t\t\tAppointmentModel.getReference().refresh();\n\t\t\t}\n\t\t});\n\t\tviewMenu.add(strikeMI);\n\t\t\n\t\t\n\t\tmenuBar.add(viewMenu);\n\n\t\t/*\n\t\t * category menu\n\t\t */\n\t\tmenuBar.add(CategoryChooser.getReference().getCategoryMenu());\n\n\t\t/*\n\t\t * \n\t\t * Import/Export Menu\n\t\t */\n\t\tJMenu impexpMenu = new JMenu();\n\t\t// impexpMenu.setIcon(IconHelper.getIcon(\n\t\t// \"/resource/Export16.gif\")));\n\t\tResourceHelper.setText(impexpMenu, \"impexpMenu\");\n\n\t\t\n\t\t\n\t\tJMenuItem exportMI = new JMenuItem();\n\t\texportMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\texportMIActionPerformed();\n\t\t\t}\n\t\t});\n\t\texportMI.setIcon(IconHelper.getIcon(\"/resource/Export16.gif\"));\n\t\tResourceHelper.setText(exportMI, \"expXML\");\n\t\timpexpMenu.add(exportMI);\n\t\t\n\t\tJMenuItem importZipMI = new JMenuItem();\n\t\tResourceHelper.setText(importZipMI, \"import_zip\");\n\t\timportZipMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\timportZipMIActionPerformed();\n\t\t\t}\n\t\t});\n\t\timportZipMI.setIcon(IconHelper.getIcon(\"/resource/Import16.gif\"));\n\t\timpexpMenu.add(importZipMI);\n\n\t\t\n\t\tJMenuItem importMI = new JMenuItem();\n\t\timportMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\timportMIActionPerformed();\n\t\t\t}\n\t\t});\n\t\timportMI.setIcon(IconHelper.getIcon(\"/resource/Import16.gif\"));\n\t\tResourceHelper.setText(importMI, \"impXML\");\n\t\timpexpMenu.add(importMI);\n\n\n\t\tmenuBar.add(impexpMenu);\n\n\t\t/*\n\t\t * \n\t\t * Undo Menu\n\t\t */\n\t\tmenuBar.add(getUndoMenu());\n\n\t\tmenuBar.add(SyncModule.getIcalMenu());\n\t\t/*\n\t\t * plugin menu\n\t\t */\n\t\tmenuBar.add(getPluginMenu());\n\n\t\t/*\n\t\t * spacing\n\t\t */\n\t\tmenuBar.add(Box.createHorizontalGlue());\n\n\t\t/*\n\t\t * \n\t\t * Help Menu\n\t\t */\n\t\thelpmenu.setIcon(IconHelper.getIcon(\"/resource/Help16.gif\"));\n\t\tResourceHelper.setText(helpmenu, \"Help\");\n\n\t\tJMenuItem helpMI = new JMenuItem();\n\t\tResourceHelper.setText(helpMI, \"Help\");\n\t\thelpMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tHelpLauncher.launchHelp();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\thelpmenu.add(helpMI);\n\n\t\tJMenuItem dbMI = new JMenuItem();\n\t\tResourceHelper.setText(dbMI, \"DatabaseInformation\");\n\t\tdbMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tdbMIActionPerformed();\n\t\t\t}\n\t\t});\n\t\thelpmenu.add(dbMI);\n\n\t\tJMenuItem AboutMI = new JMenuItem();\n\t\tResourceHelper.setText(AboutMI, \"About\");\n\t\tAboutMI.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tAboutMIActionPerformed();\n\t\t\t}\n\t\t});\n\n\t\thelpmenu.add(AboutMI);\n\t\thelpmenu.insertSeparator(helpmenu.getItemCount() - 1);\n\n\t\tmenuBar.add(helpmenu);\n\n\t}\n\n\tprivate static void importZipMIActionPerformed() {\n\n\t\tJFileChooser chooser = new JFileChooser();\n\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(\"*.zip,*.ZIP\", \"zip\", \"ZIP\");\n\t\tchooser.setFileFilter(filter);\n\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\tchooser.setDialogTitle(Resource.getResourceString(\"Please_choose_File_to_Import_From\"));\n\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\tint returnVal = chooser.showOpenDialog(null);\n\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\treturn;\n\n\t\tString fileName = chooser.getSelectedFile().getAbsolutePath();\n\t\tIOHelper.setHomeDirectory(fileName);\n\n\t\ttry {\n\t\t\tExportImport.importFromZip(fileName);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * Add an action to the action menu\n\t * \n\t * @param icon        the icon for the menu item\n\t * @param text        the text for the menu item\n\t * @param action      the action listener for the menu item\n\t * @param insertIndex the index to insert the menu item at\n\t */\n\tpublic void addAction(Icon icon, String text, ActionListener action, int insertIndex) {\n\t\tJMenuItem item = new JMenuItem();\n\t\titem.setIcon(icon);\n\t\titem.setText(text);\n\t\titem.addActionListener(action);\n\n\t\tactionMenu.insert(item, insertIndex);\n\t}\n\n\t/**\n\t * add an item to the help menu\n\t * \n\t * @param icon   the icon for the menu item\n\t * @param text   the menu item text\n\t * @param action the menu item action\n\t */\n\tpublic void addHelpMenuItem(Icon icon, String text, ActionListener action) {\n\t\tJMenuItem item = new JMenuItem();\n\t\titem.setIcon(icon);\n\t\titem.setText(text);\n\t\titem.addActionListener(action);\n\n\t\t// always insert above About item and its separator bar\n\t\thelpmenu.insert(item, helpmenu.getItemCount() - 2);\n\t}\n\n\t/**\n\t * add a menu item to the options menu\n\t * \n\t * @param item the item\n\t */\n\tpublic void addOptionsMenuItem(JMenuItem item) {\n\t\toptionsMenu.add(item);\n\t}\n\n\t/**\n\t * show the about window\n\t * \n\t */\n\tstatic public void AboutMIActionPerformed() {\n\n\t\t// show the About data\n\t\tString build_info = \"\";\n\t\tString version = \"\";\n\t\ttry {\n\t\t\t// get the version and build info from a properties file in the\n\t\t\t// jar\n\t\t\t// file\n\t\t\tInputStream is = MainMenu.class.getResource(\"/properties\").openStream();\n\t\t\tProperties props = new Properties();\n\t\t\tprops.load(is);\n\t\t\tis.close();\n\t\t\tversion = Resource.getVersion();\n\t\t\tbuild_info = Resource.getResourceString(\"Build_Time:_\") + props.getProperty(\"build.time\");\n\t\t\tbuild_info += \"\\nGit: \" + props.getProperty(\"build.number\");\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\t// build and show the version info.\n\n\t\tString info = Resource.getResourceString(\"Berger-Organizer_v\") + version + \"\\n\"\n\t\t\t\t+ Resource.getResourceString(\"copyright\") + \" Michael Berger\\n\\n\"\n\t\t\t\t+ Resource.getResourceString(\"contributions_by\") + \"\\n\" + Resource.getResourceString(\"contrib\") + \"\\n\"\n\t\t\t\t+ Resource.getResourceString(\"translations\") + \"\\n\\n\" + build_info + \"\\n\" + \"Java \"\n\t\t\t\t+ System.getProperty(\"java.version\");\n\t\tObject[] opts = { Resource.getResourceString(\"Dismiss\") };\n\t\tJOptionPane.showOptionDialog(null, info, Resource.getResourceString(\"About_BORG\"), JOptionPane.YES_NO_OPTION,\n\t\t\t\tJOptionPane.INFORMATION_MESSAGE, IconHelper.getIcon(\"/resource/borg.jpg\"), opts,\n\t\t\t\topts[0]);\n\n\t}\n\n\t/** show database info */\n\tprivate static void dbMIActionPerformed() {\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\n\t\tString info = Resource.getResourceString(\"DatabaseInformation\") + \":\\n\\n\";\n\t\tinfo += dbtype + \" URL: \" + DBHelper.getController().buildURL() + \"\\n\\n\";\n\n\t\ttry {\n\t\t\tfor (Model model : Model.getExistingModels()) {\n\t\t\t\tinfo += model.getInfo() + \"\\n\";\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t\tScrolledDialog.showNotice(info);\n\t}\n\n\t/** export */\n\tprivate static void exportMIActionPerformed() {\n\n\t\t// user wants to export the task and calendar DBs to an XML file\n\t\tString s;\n\t\twhile (true) {\n\t\t\t// prompt for a directory to store the files\n\t\t\tJFileChooser chooser = new JFileChooser();\n\n\t\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"Please_choose_directory_to_place_XML_files\"));\n\t\t\tchooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\t\t\tchooser.setApproveButtonText(Resource.getResourceString(\"select_export_dir\"));\n\n\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\treturn;\n\n\t\t\ts = chooser.getSelectedFile().getAbsolutePath();\n\t\t\tIOHelper.setHomeDirectory(s);\n\t\t\tFile dir = new File(s);\n\t\t\tString err = null;\n\t\t\tif (!dir.exists()) {\n\t\t\t\terr = Resource.getResourceString(\"Directory_[\") + s + Resource.getResourceString(\"]_does_not_exist\");\n\t\t\t} else if (!dir.isDirectory()) {\n\t\t\t\terr = \"[\" + s + Resource.getResourceString(\"]_is_not_a_directory\");\n\t\t\t}\n\n\t\t\tif (err == null)\n\t\t\t\tbreak;\n\n\t\t\tErrmsg.getErrorHandler().notice(err);\n\t\t}\n\n\t\ttry {\n\t\t\tExportImport.exportToZip(s);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/** export preferences to an XML file */\n\tprivate static void expPrefs() {\n\t\tFile file;\n\t\twhile (true) {\n\t\t\t// prompt for a file\n\t\t\tJFileChooser chooser = new JFileChooser();\n\n\t\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\tint returnVal = chooser.showSaveDialog(null);\n\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\treturn;\n\n\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\t\t\tIOHelper.setHomeDirectory(s);\n\t\t\tfile = new File(s);\n\n\t\t\tbreak;\n\n\t\t}\n\n\t\ttry {\n\t\t\tif (checkOverwrite(file.getAbsolutePath()) == true)\n\t\t\t\tPrefs.export(file.getAbsolutePath());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\tstatic public boolean checkOverwrite(String fname) {\n\n\t\tFile f = new File(fname);\n\t\tif (!f.exists())\n\t\t\treturn true;\n\n\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"overwrite_warning\") + fname + \" ?\", \"confirm_overwrite\",\n\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\treturn ret == JOptionPane.OK_OPTION;\n\t}\n\n\t/**\n\t * get the menu bar\n\t * \n\t * @return the menu bar\n\t */\n\tpublic JMenuBar getMenuBar() {\n\n\t\treturn menuBar;\n\t}\n\n\t/** plugin menu */\n\tprivate JMenu getPluginMenu() {\n\t\tif (pluginMenu == null) {\n\t\t\tpluginMenu = new JMenu();\n\t\t\tpluginMenu.setText(Resource.getResourceString(\"Plugins\"));\n\t\t\tpluginMenu.setIcon(IconHelper.getIcon(\"/resource/Preferences16.gif\"));\n\t\t\tpluginMenu.setVisible(false);\n\t\t}\n\t\treturn pluginMenu;\n\t}\n\n\t/**\n\t * add a sub menu to the plugin menu\n\t * \n\t * @param menu the sub menu\n\t */\n\tpublic void addPluginSubMenu(JMenu menu) {\n\t\tpluginMenu.add(menu);\n\t\tpluginMenu.setVisible(true);\n\t}\n\n\t/** undo menu */\n\tprivate JMenu getUndoMenu() {\n\t\tJMenu m = new JMenu();\n\t\tm.setText(Resource.getResourceString(\"undo\"));\n\t\t// m.setIcon(IconHelper.getIcon(\n\t\t// \"/resource/Refresh16.gif\")));\n\t\tfinal JMenu menu = m;\n\t\tm.addMenuListener(new MenuListener() {\n\n\t\t\t@Override\n\t\t\tpublic void menuCanceled(MenuEvent arg0) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void menuDeselected(MenuEvent arg0) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void menuSelected(MenuEvent e) {\n\t\t\t\tmenu.removeAll();\n\n\t\t\t\tfinal String top = UndoLog.getReference().getTopItem();\n\t\t\t\tif (top != null) {\n\t\t\t\t\tJMenuItem mi = new JMenuItem(Resource.getResourceString(\"undo\") + \": \" + top);\n\t\t\t\t\tmi.addActionListener(new ActionListener() {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\t\t\tResource.getResourceString(\"undo\") + \": \" + top + \"\\n\\n\"\n\t\t\t\t\t\t\t\t\t\t\t+ Resource.getResourceString(\"please_confirm\"),\n\t\t\t\t\t\t\t\t\t\"\", JOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tUndoLog.getReference().executeUndo();\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t});\n\t\t\t\t\tmenu.add(mi);\n\t\t\t\t\tJMenuItem cmi = new JMenuItem();\n\t\t\t\t\tcmi.setText(Resource.getResourceString(\"clear_undos\"));\n\t\t\t\t\tcmi.addActionListener(new ActionListener() {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\t\t\tResource.getResourceString(\"clear_undos\") + \"\\n\\n\"\n\t\t\t\t\t\t\t\t\t\t\t+ Resource.getResourceString(\"please_confirm\"),\n\t\t\t\t\t\t\t\t\t\"\", JOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\tUndoLog.getReference().clear();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t});\n\t\t\t\t\tmenu.add(cmi);\n\n\t\t\t\t\tboolean show_stack = Prefs.getBoolPref(PrefName.SHOW_UNDO_STACK);\n\t\t\t\t\tif (show_stack == true) {\n\t\t\t\t\t\tJMenu all_mi = new JMenu(Resource.getResourceString(\"all_undos\"));\n\t\t\t\t\t\tfor (String item : UndoLog.getReference().getItemStrings()) {\n\t\t\t\t\t\t\tJMenuItem item_mi = new JMenuItem(Resource.getResourceString(\"undo\") + \": \" + item);\n\t\t\t\t\t\t\tall_mi.add(item_mi);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmenu.add(all_mi);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tmenu.add(new JMenuItem(Resource.getResourceString(\"no_undos\")));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t});\n\n\t\treturn m;\n\t}\n\n\t/** import from file */\n\tprivate static void importMIActionPerformed() {\n\t\ttry {\n\n\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(Resource.getResourceString(\"xml_file\"), \"xml\",\n\t\t\t\t\t\"XML\");\n\t\t\tchooser.setFileFilter(filter);\n\t\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"Please_choose_File_to_Import_From\"));\n\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\treturn;\n\n\t\t\tString fileName = chooser.getSelectedFile().getAbsolutePath();\n\t\t\tIOHelper.setHomeDirectory(fileName);\n\n\t\t\tBufferedReader in = new BufferedReader(new FileReader(new File(fileName)));\n\t\t\tModel model = ExportImport.getImportModelForXML(in);\n\t\t\tin.close();\n\t\t\tif (model == null) {\n\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"import_format_error\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Importing_\") + \" \" + model.getExportName() + \", OK?\",\n\t\t\t\t\tResource.getResourceString(\"Import_WARNING\"), JOptionPane.OK_CANCEL_OPTION);\n\n\t\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tExportImport.importFromXmlFile(model, new FileInputStream(fileName));\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.logError(e);\n\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"Import_error\"));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/** import preferences */\n\tprivate static void impPrefs() {\n\t\tFile file;\n\t\twhile (true) {\n\t\t\t// prompt for a file\n\t\t\tJFileChooser chooser = new JFileChooser();\n\n\t\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\treturn;\n\n\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\t\t\tIOHelper.setHomeDirectory(s);\n\t\t\tfile = new File(s);\n\n\t\t\tbreak;\n\n\t\t}\n\n\t\ttry {\n\t\t\tPrefs.importPrefs(file.getAbsolutePath());\n\t\t} catch (Exception e) {\n\n\t\t\tString err = Resource.getResourceString(\"import_format_error\") + \": \" + e.getLocalizedMessage();\n\t\t\tErrmsg.getErrorHandler().notice(err);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/MultiView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Frame;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Toolkit;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JComponent;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuBar;\nimport javax.swing.JMenuItem;\nimport javax.swing.JTabbedPane;\nimport javax.swing.JToolBar;\nimport javax.swing.KeyStroke;\nimport javax.swing.UIManager;\nimport javax.swing.border.BevelBorder;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.ui.options.StartupViewsOptionsPanel;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.JTabbedPaneWithCloseIcons;\n\n/**\n * This is the main Borg UI class. It provides the the main borg tabbed window.\n */\npublic class MultiView extends View {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The size of the main borg window. */\n\tstatic private final PrefName MULTIVIEWSIZE = new PrefName(\"dayviewsize\",\n\t\t\t\"-1,-1,-1,-1,Y\");\n\n\t/**\n\t * interface implemented by all UI Modules. The MultiView manages a set of\n\t * UI Modules. Each Module is responsible for providing a component to show\n\t * in a multiview tab, responding to print requests, and requesting its own\n\t * toolbar and menu items\n\t * \n\t */\n\tpublic interface Module {\n\n\t\t/**\n\t\t * get the module's name\n\t\t * \n\t\t * @return the name\n\t\t */\n\t\tString getModuleName();\n\n\t\t/**\n\t\t * get the modules ViewType\n\t\t * \n\t\t * @return the ViewType\n\t\t */\n\t\tViewType getViewType();\n\n\t\t/**\n\t\t * get the Component for this Module\n\t\t * \n\t\t * @return the Component or null if none to show\n\t\t */\n\t\tComponent getComponent();\n\n\t\t/**\n\t\t * print the Module\n\t\t */\n\t\tvoid print();\n\n\t\t/**\n\t\t * called by the parent Multiview to allow the Module to initialize its\n\t\t * toolbar items, menu items, and anything else that must be initalized\n\t\t * before its Module methods can be called\n\t\t * \n\t\t * @param parent\n\t\t *            the parent MultiView\n\t\t */\n\t\tvoid initialize(MultiView parent);\n\t}\n\n\t/**\n\t * Interface implemented by Calendar Modules that act as modules but also\n\t * react to requests to change the shown date\n\t * \n\t */\n\tpublic interface CalendarModule extends Module {\n\t\t/**\n\t\t * update the module to show a particular date\n\t\t * \n\t\t * @param cal\n\t\t */\n\t\tvoid goTo(Calendar cal);\n\t}\n\n\t/** argument values for setView() */\n\tpublic enum ViewType {\n\t\tDAY, MONTH, WEEK, YEAR, TASK, MEMO, SEARCH, TODO, ADDRESS, CHECKLIST\n\t}\n\n\t/** The main view singleton */\n\tstatic private MultiView mainView = null;\n\n\t/**\n\t * Get the main view singleton. Make it visible if it is not showing.\n\t * \n\t * @return the main view singleton\n\t */\n\tpublic static MultiView getMainView() {\n\t\tif (mainView == null)\n\t\t\tmainView = new MultiView();\n\t\treturn (mainView);\n\t}\n\n\t/**\n\t * toolbar\n\t */\n\tprivate final JToolBar bar = new JToolBar();\n\n\t/**\n\t * the main menu\n\t */\n\tprivate final MainMenu mainMenu = new MainMenu();\n\n\t/**\n\t * Set of all modules ordered by the Module ordering number\n\t */\n\tprivate final List<Module> moduleSet = new ArrayList<Module>();\n\n\t/** The tabs */\n\tprivate JTabbedPaneWithCloseIcons tabs_ = null;\n\n\t/**\n\t * Instantiates a new multi view.\n\t */\n\tprivate MultiView() {\n\t\tsuper();\n\n\t\thandleMinimumSize(this);\n\n\t\t// escape key closes the window\n\t\tgetLayeredPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\tif (TrayIconProxy.hasTrayIcon())\n\t\t\t\t\tcloseMainwindow();\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\n\t\t// delete key removes tabs\n\t\tgetLayeredPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\tComponent c = getTabs().getSelectedComponent();\n\t\t\t\tgetTabs().remove(c);\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\t\t\n\t\tgetLayeredPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tHelpLauncher.launchHelp();\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t}\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\n\t\t// window close button closes the window\n\t\taddWindowListener(new java.awt.event.WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(java.awt.event.WindowEvent evt) {\n\t\t\t\tcloseMainwindow();\n\t\t\t}\n\t\t});\n\n\t\t// create the menu bar\n\t\tJMenuBar menubar = mainMenu.getMenuBar();\n\t\tmenubar.setBorder(new BevelBorder(BevelBorder.RAISED));\n\t\tsetJMenuBar(menubar);\n\t\tgetContentPane().setLayout(new GridBagLayout());\n\n\t\tmainMenu.addAction(\n\t\t\t\tIconHelper.getIcon(\"/resource/Print16.gif\"),\n\t\t\t\tResource.getResourceString(\"Print\"), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tprint();\n\t\t\t\t\t}\n\t\t\t\t}, 0);\n\n\t\tmainMenu.addAction(\n\t\t\t\tUIManager.getIcon(\"InternalFrame.closeIcon\"),\n\t\t\t\tResource.getResourceString(\"close_tabs\"), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tcloseTabs();\n\t\t\t\t\t}\n\t\t\t\t}, 1);\n\n\t\t// add the tool bar\n\t\tGridBagConstraints cons = new java.awt.GridBagConstraints();\n\t\tcons.gridx = 0;\n\t\tcons.gridy = 0;\n\t\tcons.fill = java.awt.GridBagConstraints.HORIZONTAL;\n\t\tcons.weightx = 0.0;\n\t\tcons.weighty = 0.0;\n\t\tgetContentPane().add(getToolBar(), cons);\n\n\t\t// add the tabs\n\t\tcons = new java.awt.GridBagConstraints();\n\t\tcons.gridx = 0;\n\t\tcons.gridy = 1;\n\t\tcons.fill = java.awt.GridBagConstraints.BOTH;\n\t\tcons.weightx = 1.0;\n\t\tcons.weighty = 1.0;\n\t\tgetContentPane().add(getTabs(), cons);\n\n\t\tsetTitle(\"BORG \" + Resource.getVersion());\n\t\tsetDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\n\t\tpack();\n\t\tsetView(ViewType.MONTH); // start month view\n\t\tmanageMySize(MULTIVIEWSIZE);\n\t}\n\n\t/**\n\t * add a toolbar button\n\t * \n\t * @param icon\n\t *            the toolbar icon\n\t * @param tooltip\n\t *            the tooltip for the button\n\t * @param action\n\t *            the action listener for the button\n\t */\n\tpublic JButton addToolBarItem(Icon icon, String tooltip, ActionListener action) {\n\t\tJButton button = new JButton(icon);\n\t\tbutton.setToolTipText(tooltip);\n\t\tbutton.addActionListener(action);\n\t\tbar.add(button, toolBarInsertIndex);\n\n\t\tmainMenu.addAction(icon, tooltip, action, toolBarInsertIndex++);\n\t\t\n\t\treturn button;\n\t}\n\n\t/**\n\t * add an item to the options menu\n\t * \n\t * @param item\n\t *            the item to add\n\t */\n\tpublic void addOptionsMenuItem(JMenuItem item) {\n\t\tmainMenu.addOptionsMenuItem(item);\n\t}\n\n\tprivate int toolBarInsertIndex = 0;\n\n\t/**\n\t * add a help menu item\n\t * \n\t * @param icon\n\t *            the menu item icon\n\t * @param tooltip\n\t *            the text for the menu item\n\t * @param action\n\t *            the action listener for the menu item\n\t */\n\tpublic void addHelpMenuItem(Icon icon, String tooltip, ActionListener action) {\n\t\tmainMenu.addHelpMenuItem(icon, tooltip, action);\n\t}\n\n\t/**\n\t * add a plugin sub menu\n\t * \n\t * @param menu\n\t *            - the sub menu\n\t */\n\tpublic void addPluginSubMenu(JMenu menu) {\n\t\tmainMenu.addPluginSubMenu(menu);\n\t}\n\n\t/**\n\t * close the main view. If the system tray icon is active, the program stays\n\t * running. If no system tray icon is active, the program shuts down\n\t * entirely\n\t */\n\tprivate void closeMainwindow() {\n\t\tif (!TrayIconProxy.hasTrayIcon() && this == mainView) {\n\t\t\tUIControl.shutDownUI();\n\t\t} else {\n\t\t\tthis.dispose();\n\t\t}\n\t}\n\n\t/**\n\t * Close the currently selected tab.\n\t */\n\tpublic void closeSelectedTab() {\n\t\ttabs_.closeSelectedTab();\n\t}\n\n\t/**\n\t * Close all tabs.\n\t */\n\tpublic void closeTabs() {\n\t\ttabs_.closeClosableTabs();\n\t}\n\n\t/** destroy this window */\n\t@Override\n\tpublic void destroy() {\n\t\tthis.dispose();\n\t}\n\t\n\t/*\n\t * set the title of the tab that contains the given component\n\t */\n\tpublic void setTabTitle(String title, Component c)\n\t{\n\t\tint i = tabs_.indexOfComponent(c);\n\t\tif( i >= 0)\n\t\t\ttabs_.setTitleAt(i, title);\n\t\t\n\t}\n\n\t/**\n\t * Add a plain component as a tab, and do not treat as a module\n\t * \n\t * @param c\n\t *            the component\n\t */\n\tpublic void addView(String title, Component c) {\n\t\ttabs_.addTab(title, c);\n\t\ttabs_.setSelectedIndex(tabs_.getTabCount() - 1);\n\t}\n\n\t/**\n\t * remove a component from tabs\n\t *\n\t */\n\tpublic void removeView(String title) {\n\t\tint i = tabs_.indexOfTab(title);\n\t\twhile (i >= 0) {\n\t\t\ttabs_.remove(i);\n\t\t\ti = tabs_.indexOfTab(title);\n\t\t}\n\t}\n\n\t/**\n\t * get the Module for a given ViewType. called by ui components other than\n\t * the module in question that want to request a particular view\n\t * \n\t * @param type\n\t *            the view type\n\t * @return the Module or null\n\t */\n\tpublic Module getModuleForView(ViewType type) {\n\t\tfor (Module m : moduleSet) {\n\t\t\tif (type == m.getViewType())\n\t\t\t\treturn m;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<Module> getModuleSet() {\n\t\treturn moduleSet;\n\t}\n\n\t/**\n\t * Gets the tabs.\n\t * \n\t * @return the tabs\n\t */\n\tprivate JTabbedPane getTabs() {\n\t\tif (tabs_ == null) {\n\t\t\ttabs_ = new JTabbedPaneWithCloseIcons();\n\t\t}\n\t\treturn tabs_;\n\t}\n\n\t/**\n\t * Gets the tool bar.\n\t * \n\t * @return the tool bar\n\t */\n\tprivate JToolBar getToolBar() {\n\t\tbar.setFloatable(false);\n\n\t\tJButton printbut = new JButton(IconHelper.getIcon(\"/resource/Print16.gif\"));\n\t\tprintbut.setToolTipText(Resource.getResourceString(\"Print\"));\n\t\tprintbut.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tprint();\n\t\t\t}\n\t\t});\n\t\tbar.add(printbut);\n\n\t\tbar.addSeparator();\n\n\t\t//JButton clearbut = new JButton(IconHelper.getIcon(\n\t\t\t//\t\"/resource/Delete16.gif\")));\n\t\tJButton clearbut = new JButton(UIManager.getIcon(\"InternalFrame.closeIcon\"));\n\t\tclearbut.setToolTipText(Resource.getResourceString(\"close_tabs\"));\n\t\tclearbut.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tcloseTabs();\n\t\t\t}\n\t\t});\n\t\tbar.add(clearbut);\n\n\t\treturn bar;\n\t}\n\n\t/**\n\t * have the Calendar Modules show a particular day\n\t * \n\t * @param cal\n\t *            the day to show\n\t */\n\tpublic void goTo(Calendar cal) {\n\t\tfor (Module m : moduleSet) {\n\t\t\tif (m instanceof CalendarModule)\n\t\t\t\t((CalendarModule) m).goTo(cal);\n\t\t}\n\t}\n\n\t/**\n\t * add a new module to the multi view\n\t * \n\t * @param m\n\t *            the module\n\t */\n\tpublic void addModule(Module m) {\n\t\tmoduleSet.add(m);\n\t\tm.initialize(this);\n\t}\n\n\t/**\n\t * prints the currently selected tab if pritning is supported for that tab\n\t */\n\tpublic void print() {\n\n\t\tComponent c = getTabs().getSelectedComponent();\n\t\tfor (Module m : moduleSet) {\n\t\t\tif (m.getComponent() == c) {\n\t\t\t\tm.print();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"No_Print\"));\n\n\t}\n\n\t/**\n\t * refresh the view based on model changes. currently does nothing for this\n\t * view.\n\t */\n\t@Override\n\tpublic void refresh() {\n\t\t// nothing to refresh for this view\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * Sets the currently selected tab to be a particular view as defined in\n\t * ViewType.\n\t * \n\t * @param type\n\t *            the new view\n\t * @return the Component, if any that is now being displayed\n\t */\n\tpublic Component setView(ViewType type) {\n\n\t\tModule m = getModuleForView(type);\n\t\tif (m != null) {\n\t\t\tComponent component = m.getComponent();\n\t\t\tif (component != null) {\n\t\t\t\tif (component instanceof DockableView) {\n\n\t\t\t\t\t((DockableView) component).showView();\n\n\t\t\t\t\tif (((DockableView) component).isDocked()) {\n\t\t\t\t\t\tgetTabs().setSelectedComponent(component);\n\t\t\t\t\t\tif (!this.isShowing()\n\t\t\t\t\t\t\t\t|| this.getState() == Frame.ICONIFIED) {\n\t\t\t\t\t\t\tMultiView.getMainView().setVisible(true);\n\t\t\t\t\t\t\tMultiView.getMainView().toFront();\n\t\t\t\t\t\t\tMultiView.getMainView().setState(Frame.NORMAL);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn component;\n\t\t\t\t}\n\n\t\t\t\tif (!component.isDisplayable()) {\n\t\t\t\t\ttabs_.addTab(m.getModuleName(), component);\n\t\t\t\t}\n\n\t\t\t\tgetTabs().setSelectedComponent(component);\n\t\t\t\tif (!this.isShowing() || this.getState() == Frame.ICONIFIED) {\n\t\t\t\t\tMultiView.getMainView().setVisible(true);\n\t\t\t\t\tMultiView.getMainView().toFront();\n\t\t\t\t\tMultiView.getMainView().setState(Frame.NORMAL);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn component;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * open all views that are set to appear on startup\n\t */\n\tpublic void startupViews(boolean bgStart) {\n\t\tfor (ViewType vt : ViewType.values()) {\n\t\t\tif (StartupViewsOptionsPanel.getStartPref(vt)) {\n\t\t\t\tif (bgStart) {\n\n\t\t\t\t\t// starting to tray, so only start docked views, and don't\n\t\t\t\t\t// make the main view visible\n\t\t\t\t\tModule m = getModuleForView(vt);\n\t\t\t\t\tif (m != null) {\n\t\t\t\t\t\tComponent component = m.getComponent();\n\t\t\t\t\t\tif (component != null) {\n\t\t\t\t\t\t\tif (component instanceof DockableView) {\n\t\t\t\t\t\t\t\t((DockableView) component).bgStart();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttabs_.addTab(m.getModuleName(), component);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// if not starting to tray, then make views visible as\n\t\t\t\t\t// normal\n\t\t\t\t\tsetView(vt);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\tpublic void addMenu(JMenu m) {\n\t\tmainMenu.getMenuBar().add(m);\n\t}\n\n\t//Set the minimum size if the\n\t//user's display is big enough.\n\tprivate static void handleMinimumSize(MultiView mv) {\n\t\tDimension d = Toolkit.getDefaultToolkit().getScreenSize();\n\t\tdouble width = d.getWidth();\n\t\tdouble height = d.getHeight();\n\n\t\tif(width > 1000 && height >= 800) {\n\t\t\tmv.setMinimumSize(new Dimension(900, 700));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/NavPanel.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\n\nimport javax.swing.JButton;\nimport javax.swing.JPanel;\n\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.util.DateDialog;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * A NavPanel provides common navigation buttons to other classes that navigate among dates\n *\n */\npublic class NavPanel extends JPanel {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Interface to be implemented by any class that allows the attachment\n\t * of a NavPanel navigator\n\t *\n\t */\n\tpublic interface Navigator {\n\n\t\t/**\n\t\t * get the navigator label - i.e. a string indicating the current date\n\t\t * @return the text to show on the navigator main label\n\t\t */\n        String getNavLabel();\n\n\t\t/** navigate to a particular date */\n        void goTo(Calendar cal);\n\n\t\t/** go to the next item */\n        void next();\n\n\t\t/** go to the previous item */\n        void prev();\n\n\t\t/** go to today */\n        void today();\n\t}\n\n\t/** the main navigator label - showing the current date */\n\tprivate final JButton label = new JButton();\n\n\t/** the attached navigator object */\n\tprivate Navigator nav_ = null;\n\n\t/** \n\t * constructor \n\t * @param nav the Navigator object that this NavPanel will be controlling\n\t */\n\tpublic NavPanel(Navigator nav) {\n\n\t\tnav_ = nav;\n\n\t\tsetLayout(new GridBagLayout());\n\n\t\t// create the various navigator buttons\n\t\tJButton Prev = new JButton();\n\t\tPrev.setMaximumSize(new Dimension(16, 16));\n\t\tPrev.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Back16.gif\"));\n\t\tPrev.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tnav_.prev();\n\t\t\t\tlabel.setText(nav_.getNavLabel());\n\t\t\t}\n\t\t});\n\n\t\tJButton Next = new JButton();\n\t\tNext.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Forward16.gif\"));\n\t\tNext.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);\n\t\tNext.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tnav_.next();\n\t\t\t\tlabel.setText(nav_.getNavLabel());\n\t\t\t}\n\t\t});\n\n\t\tJButton Today = new JButton();\n\t\tToday.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Home16.gif\"));\n\t\tToday.setToolTipText(Resource.getResourceString(\"Today\"));\n\t\tToday.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tnav_.today();\n\t\t\t\tlabel.setText(nav_.getNavLabel());\n\t\t\t}\n\t\t});\n\n\t\tJButton Goto = new JButton();\n\t\tGoto.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Undo16.gif\"));\n\t\tGoto.setToolTipText(Resource.getResourceString(\"Go_To\"));\n\t\tGoto.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tDateDialog dlg = new DateDialog(null);\n\t\t\t\tdlg.setCalendar(new GregorianCalendar());\n\t\t\t\tdlg.setVisible(true);\n\t\t\t\tCalendar dlgcal = dlg.getCalendar();\n\t\t\t\tif (dlgcal == null)\n\t\t\t\t\treturn;\n\t\t\t\tnav_.goTo(dlgcal);\n\t\t\t\tlabel.setText(nav_.getNavLabel());\n\t\t\t}\n\t\t});\n\n\t\t// the main label\n\t\tlabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);\n\t\tlabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);\n\t\tlabel.setText(nav_.getNavLabel());\n\n\t\t// add components\n\t\tGridBagConstraints cons = new GridBagConstraints();\n\t\tcons.fill = GridBagConstraints.NONE;\n\t\tcons.gridx = 0;\n\t\tcons.gridy = 0;\n\t\tadd(Today, cons);\n\n\t\tcons.gridx = 1;\n\t\tadd(Prev, cons);\n\n\t\tcons.gridx = 2;\n\t\tcons.weightx = 1.0;\n\t\tcons.fill = GridBagConstraints.HORIZONTAL;\n\t\tadd(label, cons);\n\n\t\tcons.fill = GridBagConstraints.NONE;\n\t\tcons.weightx = 0.0;\n\t\tcons.gridx = 3;\n\t\tadd(Next, cons);\n\n\t\tcons.gridx = 4;\n\t\tadd(Goto, cons);\n\n\t}\n\n\t/** set the navigator label */\n\tpublic void setLabel(String l) {\n\t\tlabel.setText(l);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/ResourceHelper.java",
    "content": "/*\n  This file is part of BORG.\n\n  BORG is free software; you can redistribute it and/or modify\n  it under the terms of the GNU General Public License as published by\n  the Free Software Foundation; either version 2 of the License, or\n  (at your option) any later version.\n\n  BORG is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n  GNU General Public License for more details.\n\n  You should have received a copy of the GNU General Public License\n  along with BORG; if not, write to the Free Software\n  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n  Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui;\n\nimport net.sf.borg.common.Resource;\n\nimport javax.swing.*;\n\n/*\n * Disable mnemonic feature in 1.8. Feature was never properly maintained - causing problems.\n */\n/**\n * Helps parse resource strings (containing text and optional mnemonics and\n * accelerators) for GUI widgets.\n * \n * @author membar\n */\npublic class ResourceHelper {\n\n\n\tpublic static void setText(JMenu mnu, String resourceKey) {\n\t\tmnu.setText(Resource.getResourceString(resourceKey));\n\n\t}\n\n\n\tpublic static void setText(JMenuItem mnuitm, String resourceKey) {\n\t\tmnuitm.setText(Resource.getResourceString(resourceKey));\n\n\t}\n\n\n\tpublic static void setText(AbstractButton button, String resourceKey) {\n\t\tbutton.setText(Resource.getResourceString(resourceKey));\n\n\t}\n\n\n\tpublic static void setText(JLabel label, String resourceKey) {\n\t\tlabel.setText(Resource.getResourceString(resourceKey));\n\n\t}\n\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/SearchView.java",
    "content": "/*\n * This file is part of BORG. BORG is free software; you can redistribute it\n * and/or modify it under the terms of the GNU General Public License as\n * published by the Free Software Foundation; either version 2 of the License,\n * or (at your option) any later version. BORG is distributed in the hope that\n * it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details. You should have received a copy of\n * the GNU General Public License along with BORG; if not, write to the Free\n * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n * USA Copyright 2005 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JComponent;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.JTextField;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.DefaultTableCellRenderer;\nimport javax.swing.table.TableCellRenderer;\nimport javax.swing.table.TableColumn;\nimport javax.swing.table.TableColumnModel;\n\nimport com.toedter.calendar.JDateChooser;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.MemoModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.SearchCriteria;\nimport net.sf.borg.model.Searchable;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.KeyedEntity;\nimport net.sf.borg.model.entity.LabelEntity;\nimport net.sf.borg.model.entity.Memo;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.address.AddressView;\nimport net.sf.borg.ui.calendar.AppointmentListView;\nimport net.sf.borg.ui.memo.MemoPanel;\nimport net.sf.borg.ui.task.ProjectView;\nimport net.sf.borg.ui.task.TaskView;\nimport net.sf.borg.ui.util.ColorComboBox;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PlainDateEditor;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * UI for searching records.\n */\npublic class SearchView extends DockableView implements Module {\n\n    private static final long serialVersionUID = 1L;\n\n    private JCheckBox addressCheckBox = null;\n\n    private JCheckBox apptCheckBox = null;\n\n    /**\n     * The case sensitive check box.\n     */\n    private JCheckBox caseSensitiveCheckBox = null;\n\n    /**\n     * The category combo box.\n     */\n    private JComboBox<String> categoryComboBox = null;\n\n    private final ColorComboBox colorComboBox = new ColorComboBox();\n\n    /**\n     * The end date chooser.\n     */\n    private JDateChooser endDateChooser = null;\n\n    /**\n     * The holiday check box.\n     */\n    private JCheckBox holidayCheckBox = null;\n\n    /**\n     * The link check box.\n     */\n    private JCheckBox linkCheckBox = null;\n\n    private JCheckBox memoCheckBox = null;\n\n    private JCheckBox projectCheckBox = null;\n\n    /**\n     * The repeat check box.\n     */\n    private JCheckBox repeatCheckBox = null;\n\n    /**\n     * The results table.\n     */\n    private JTable resultsTable = null;\n\n    /**\n     * The search text.\n     */\n    private JTextField searchText = null;\n\n    /**\n     * The start date chooser.\n     */\n    private JDateChooser startDateChooser = null;\n\n    private JCheckBox taskCheckBox = null;\n\n    /**\n     * The todo check box.\n     */\n    private JCheckBox todoCheckBox = null;\n\n    // whole word search option\n    private JCheckBox wholeWordBox = null;\n\n    /**\n     * The vacation check box.\n     */\n    private JCheckBox vacationCheckBox = null;\n\n    private boolean isInitialized = false;\n\n    /**\n     * constructor.\n     */\n    public SearchView() {\n        super();\n\n    }\n\n    /**\n     * Adds user colors to the search table\n     */\n    class SearchTableCellRenderer extends DefaultTableCellRenderer {\n\n        private TableCellRenderer originalRenderer = null;\n\n        private static final long serialVersionUID = 1L;\n\n        SearchTableCellRenderer(TableCellRenderer orig) {\n            originalRenderer = orig;\n        }\n\n\n        @Override\n        public Component getTableCellRendererComponent(JTable table,\n                                                       Object value, boolean isSelected, boolean hasFocus, int row,\n                                                       int column) {\n\n            Component c = originalRenderer.getTableCellRendererComponent(table,\n                    value, isSelected, hasFocus, row, column);\n\n            DateFormat sdf = DateFormat.getDateInstance();\n\n            JLabel theTableCellComponent = (JLabel) c;\n\n            // user colors\n\n            if (isSelected) {\n                theTableCellComponent.setForeground(Color.BLACK);\n                theTableCellComponent.setBackground(Color.ORANGE);\n            } else {\n\n                Theme t = Theme.getCurrentTheme();\n                String color = (String) table.getModel().getValueAt(row, 5);\n\n                theTableCellComponent.setForeground(new Color(t.colorFromString(color)));\n                theTableCellComponent.setBackground(new Color(t\n                        .getWeekdayBg()));\n\n            }\n\n            if (column == 2 && value != null) {\n\n                // format dates\n                String dateText = sdf.format(value);\n                if (!DateUtil.isAfter((Date) value, new Date()))\n                    dateText = \"* \" + dateText;\n                theTableCellComponent.setText(dateText);\n            }\n            return theTableCellComponent;\n        }\n    }\n\n\n    private void loadCategories() {\n        // populate the category combo box\n        categoryComboBox.removeAllItems();\n        categoryComboBox.addItem(\"\");\n        try {\n            Collection<String> cats = CategoryModel.getReference().getCategories();\n            Iterator<String> it = cats.iterator();\n            while (it.hasNext()) {\n                categoryComboBox.addItem(it.next());\n            }\n            categoryComboBox.setSelectedIndex(0);\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n\n    }\n\n    /**\n     * create the change category button.\n     *\n     * @return the change category button\n     */\n    private JButton createChangeCategoryButton() {\n        JButton changeCategoryButton = new JButton();\n        ResourceHelper.setText(changeCategoryButton, \"chg_cat\");\n        changeCategoryButton.setIcon(IconHelper.getIcon(\"/resource/Preferences16.gif\")); // Generated\n        changeCategoryButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n\n                // change the category of all selected rows\n                int[] rows = resultsTable.getSelectedRows();\n                if (rows.length == 0) {\n                    return;\n                }\n\n                try {\n\n                    Collection<String> allcats = CategoryModel.getReference().getCategories();\n                    Object[] cats = allcats.toArray();\n\n                    // ask the user to choose the new category\n                    Object o = JOptionPane.showInputDialog(null, Resource.getResourceString(\"cat_choose\"), \"\",\n                            JOptionPane.QUESTION_MESSAGE, null, cats, cats[0]);\n                    if (o == null)\n                        return;\n\n                    String cat = (String) o;\n                    if (cat.isEmpty() || CategoryModel.UNCATEGORIZED.equals(cat))\n                        cat = null;\n\n                    TableSorter tm = (TableSorter) resultsTable.getModel();\n\n                    // get a list of selected items\n                    ArrayList<KeyedEntity<?>> entities = new ArrayList<KeyedEntity<?>>();\n                    for (int i = 0; i < rows.length; i++) {\n                        Integer key = (Integer) tm.getValueAt(rows[i], 3);\n                        Class<?> cl = (Class<?>) tm.getValueAt(rows[i], 4);\n                        if( cl == LabelEntity.class)\n                        \tcontinue;\n                        try {\n                            KeyedEntity<?> ent = (KeyedEntity<?>) cl.getDeclaredConstructor().newInstance();\n                            ent.setKey(key.intValue());\n                            entities.add(ent);\n                        } catch (Exception e1) {\n                            Errmsg.getErrorHandler().errmsg(e1);\n                        }\n\n                    }\n\n                    // change the categories\n                    for (KeyedEntity<?> ent : entities) {\n                        if (ent instanceof Appointment) {\n                            Appointment ap = AppointmentModel.getReference().getAppt(ent.getKey());\n                            ap.setCategory(cat);\n                            AppointmentModel.getReference().saveAppt(ap);\n                        }\n                        if (ent instanceof Project) {\n                            Project ap = TaskModel.getReference().getProject(ent.getKey());\n                            ap.setCategory(cat);\n                            TaskModel.getReference().saveProject(ap);\n                        }\n                        if (ent instanceof Task) {\n                            Task ap = TaskModel.getReference().getTask(ent.getKey());\n                            ap.setCategory(cat);\n                            TaskModel.getReference().savetask(ap);\n                        }\n\n                    }\n\n                } catch (Exception ex) {\n                    Errmsg.getErrorHandler().errmsg(ex);\n                    return;\n                }\n\n                refresh(); // refresh results\n            }\n        });\n\n        return changeCategoryButton;\n    }\n\n\n    private JButton createChangeColorButton() {\n        JButton changeColorButton = new JButton();\n        ResourceHelper.setText(changeColorButton, \"chg_color\");\n        changeColorButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n\n                // change the category of all selected rows\n                int[] rows = resultsTable.getSelectedRows();\n                if (rows.length == 0) {\n                    return;\n                }\n\n                try {\n\n\n                    TableSorter tm = (TableSorter) resultsTable.getModel();\n\n                    // get a list of selected items\n                    ArrayList<KeyedEntity<?>> entities = new ArrayList<KeyedEntity<?>>();\n                    for (int i = 0; i < rows.length; i++) {\n                        Integer key = (Integer) tm.getValueAt(rows[i], 3);\n                        Class<?> cl = (Class<?>) tm.getValueAt(rows[i], 4);\n                        if( cl == LabelEntity.class)\n                        \tcontinue;\n                        try {\n                            KeyedEntity<?> ent = (KeyedEntity<?>) cl.getDeclaredConstructor().newInstance();\n                            ent.setKey(key.intValue());\n                            entities.add(ent);\n                        } catch (Exception e1) {\n                            Errmsg.getErrorHandler().errmsg(e1);\n                        }\n\n                    }\n\n                    String colorString = (String) colorComboBox.getSelectedItem();\n                    if (colorString.equals(\"chosen\"))\n                        colorString = Integer.toString(colorComboBox.getChosenColor().getRGB());\n\n                    boolean warn = false;\n                    // change the categories\n                    for (KeyedEntity<?> ent : entities) {\n                        if (ent instanceof Appointment) {\n                            Appointment ap = AppointmentModel.getReference().getAppt(ent.getKey());\n                            ap.setColor(colorString);\n                            AppointmentModel.getReference().saveAppt(ap);\n                        } else {\n                            warn = true;\n                        }\n                    }\n\n                    if( warn == true){\n                        Errmsg.getErrorHandler().notice(Resource.getResourceString(\"search_color_warn\"));\n                    }\n\n                } catch (Exception ex) {\n                    Errmsg.getErrorHandler().errmsg(ex);\n                    return;\n                }\n\n                refresh(); // refresh results\n\n\n            }\n        });\n\n        return changeColorButton;\n    }\n\n    /**\n     * creates the check box panel\n     *\n     * @return the check box panel\n     */\n    private JPanel createCheckBoxPanel() {\n        JPanel checkBoxPanel = new JPanel();\n        checkBoxPanel.setLayout(new GridBagLayout());\n\n        apptCheckBox = new JCheckBox();\n        apptCheckBox.setSelected(true);\n        ResourceHelper.setText(apptCheckBox, \"appointment\");\n        checkBoxPanel.add(apptCheckBox, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n        taskCheckBox = new JCheckBox();\n        taskCheckBox.setSelected(true);\n        ResourceHelper.setText(taskCheckBox, \"task\");\n        checkBoxPanel.add(taskCheckBox, GridBagConstraintsFactory.create(2, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n        addressCheckBox = new JCheckBox();\n        addressCheckBox.setSelected(true);\n        ResourceHelper.setText(addressCheckBox, \"Address\");\n        checkBoxPanel.add(addressCheckBox, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n        projectCheckBox = new JCheckBox();\n        projectCheckBox.setSelected(true);\n        ResourceHelper.setText(projectCheckBox, \"project\");\n        checkBoxPanel.add(projectCheckBox, GridBagConstraintsFactory.create(3, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n        memoCheckBox = new JCheckBox();\n        memoCheckBox.setSelected(true);\n        ResourceHelper.setText(memoCheckBox, \"memo\");\n        checkBoxPanel.add(memoCheckBox, GridBagConstraintsFactory.create(4, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        todoCheckBox = new JCheckBox();\n        ResourceHelper.setText(todoCheckBox, \"To_Do\");\n        checkBoxPanel.add(todoCheckBox, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        repeatCheckBox = new JCheckBox();\n        ResourceHelper.setText(repeatCheckBox, \"repeating\");\n        checkBoxPanel.add(repeatCheckBox, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        vacationCheckBox = new JCheckBox();\n        ResourceHelper.setText(vacationCheckBox, \"Vacation\");\n        checkBoxPanel.add(vacationCheckBox, GridBagConstraintsFactory.create(2, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        holidayCheckBox = new JCheckBox();\n        ResourceHelper.setText(holidayCheckBox, \"Holiday\");\n        checkBoxPanel.add(holidayCheckBox, GridBagConstraintsFactory.create(3, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        linkCheckBox = new JCheckBox();\n        linkCheckBox.setText(Resource.getResourceString(\"haslinks\"));\n        checkBoxPanel.add(linkCheckBox, GridBagConstraintsFactory.create(4, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n        return checkBoxPanel;\n    }\n\n    /**\n     * create the delete button.\n     *\n     * @return the delete button\n     */\n    private JButton createDeleteButton() {\n        JButton deleteButton = new JButton();\n        ResourceHelper.setText(deleteButton, \"delete_selected\");\n        deleteButton.setIcon(IconHelper.getIcon(\"/resource/Stop16.gif\"));\n        deleteButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n\n                // delete all selected rows\n\n                int[] rows = resultsTable.getSelectedRows();\n                if (rows.length == 0) {\n                    return;\n                }\n\n                // confirm delete\n                int ret = JOptionPane.showConfirmDialog(null, Resource.getResourceString(\"delete_selected\") + \"?\", \"\",\n                        JOptionPane.YES_NO_OPTION);\n                if (ret != JOptionPane.YES_OPTION) {\n                    return;\n                }\n\n                // get selected items\n                TableSorter tm = (TableSorter) resultsTable.getModel();\n                ArrayList<Object> entities = new ArrayList<Object>();\n                for (int i = 0; i < rows.length; i++) {\n                    Integer key = (Integer) tm.getValueAt(rows[i], 3);\n                    Class<?> cl = (Class<?>) tm.getValueAt(rows[i], 4);\n                    try {\n                        Object ent = cl.getDeclaredConstructor().newInstance();\n                        if (ent instanceof KeyedEntity) {\n                            ((KeyedEntity<?>) ent).setKey(key.intValue());\n                        } else if (ent instanceof Memo)\n                            ((Memo) ent).setMemoName((String) tm.getValueAt(rows[i], 0));\n                        entities.add(ent);\n                    } catch (Exception e1) {\n                        Errmsg.getErrorHandler().errmsg(e1);\n                    }\n\n                }\n\n                // delete the items\n                for (Object ent : entities) {\n                    if (ent instanceof Appointment)\n                        AppointmentModel.getReference().delAppt(((Appointment) ent).getKey());\n                    else if (ent instanceof Address)\n                        AddressModel.getReference().delete((Address) ent);\n                    else if (ent instanceof Memo)\n                        MemoModel.getReference().delete(((Memo) ent).getMemoName(), false);\n                }\n\n                refresh(); // reload results\n            }\n        });\n\n        return deleteButton;\n    }\n\n    /**\n     * create the results panel.\n     *\n     * @return the results panel\n     */\n    private JPanel createResultsPanel() {\n\n        JPanel resultsPanel = new JPanel();\n\n        resultsPanel.setLayout(new GridBagLayout());\n        resultsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null,\n                Resource.getResourceString(\"Search_Results\"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,\n                javax.swing.border.TitledBorder.DEFAULT_POSITION, null, null));\n\n        resultsTable = new JTable();\n        resultsTable.setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n        resultsTable.addMouseListener(new java.awt.event.MouseAdapter() {\n            @Override\n            public void mouseClicked(java.awt.event.MouseEvent e) {\n                if (e.getClickCount() < 2)\n                    return;\n                try {\n                    showSelectedItem();\n                } catch (Exception e1) {\n                    Errmsg.getErrorHandler().errmsg(e1);\n                }\n            }\n        });\n\n        JScrollPane tableScroll = new JScrollPane();\n        tableScroll.setViewportView(resultsTable);\n        resultsTable.setShowGrid(true);\n        resultsTable.setIntercellSpacing(new Dimension(1, 1));\n        // tableScroll.setPreferredSize(new java.awt.Dimension(100, 100));\n\n        GridBagConstraints gridBagConstraints2 = GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0,\n                1.0);\n        gridBagConstraints2.gridwidth = 4;\n        resultsPanel.add(tableScroll, gridBagConstraints2);\n        resultsPanel.add(createDeleteButton(),\n                GridBagConstraintsFactory.create(0, 1, GridBagConstraints.NONE, 1.0, 0.0));\n        resultsPanel.add(createChangeCategoryButton(),\n                GridBagConstraintsFactory.create(1, 1, GridBagConstraints.NONE, 1.0, 0.0));\n        JPanel colorPanel = new JPanel();\n        colorPanel.add(createChangeColorButton());\n        colorPanel.add(colorComboBox);\n        resultsPanel.add(colorPanel,\n                GridBagConstraintsFactory.create(2, 1, GridBagConstraints.NONE, 1.0, 0.0));\n        JButton csvButton = new JButton();\n        csvButton.setText(\"CSV\");\n        csvButton.addActionListener(new ActionListener() {\n\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                try {\n                    // prompt for a file\n                    JFileChooser chooser = new JFileChooser();\n                    chooser.setCurrentDirectory(new File(\".\"));\n                    chooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n                    chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n                    int returnVal = chooser.showSaveDialog(null);\n                    if (returnVal != JFileChooser.APPROVE_OPTION)\n                        return;\n\n                    String s = chooser.getSelectedFile().getAbsolutePath();\n\n                    // auto append extension\n                    if (chooser.getFileFilter() != chooser.getAcceptAllFileFilter()) {\n                        if (!s.contains(\".\")) {\n                            s += \".csv\";\n                        }\n                    }\n\n                    FileWriter w = new FileWriter(s);\n                    exportToCSV(w);\n                } catch (IOException e1) {\n                    Errmsg.getErrorHandler().errmsg(e1);\n                }\n\n            }\n        });\n        resultsPanel.add(csvButton, GridBagConstraintsFactory.create(3, 1, GridBagConstraints.NONE, 1.0, 0.0));\n\n        return resultsPanel;\n    }\n\n    /**\n     * Creates the search criteria panel.\n     *\n     * @return the search criteria panel.\n     */\n    private JPanel createSearchCriteriaPanel() {\n\n        JPanel searchCriteriaPanel = new JPanel();\n        searchCriteriaPanel.setLayout(new GridBagLayout());\n        searchCriteriaPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, \"\",\n                javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION,\n                null, null));\n\n        JLabel searchStringLabel = new JLabel();\n        ResourceHelper.setText(searchStringLabel, \"SearchString\");\n        searchCriteriaPanel.add(searchStringLabel,\n                GridBagConstraintsFactory.create(0, 0, GridBagConstraints.HORIZONTAL));\n\n        JLabel categoryLabel = new JLabel();\n        ResourceHelper.setText(categoryLabel, \"Category\");\n        searchCriteriaPanel.add(categoryLabel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.HORIZONTAL));\n\n        searchText = new JTextField();\n        searchCriteriaPanel.add(searchText,\n                GridBagConstraintsFactory.create(1, 0, GridBagConstraints.HORIZONTAL, 1.0, 0.0));\n\n        categoryComboBox = new JComboBox<String>();\n        searchCriteriaPanel.add(categoryComboBox,\n                GridBagConstraintsFactory.create(1, 1, GridBagConstraints.HORIZONTAL, 1.0, 0.0));\n\n        caseSensitiveCheckBox = new JCheckBox();\n        caseSensitiveCheckBox.setText(Resource.getResourceString(\"case_sensitive\"));\n        searchCriteriaPanel.add(caseSensitiveCheckBox, GridBagConstraintsFactory.create(2, 0, GridBagConstraints.BOTH));\n\n        wholeWordBox = new JCheckBox();\n        wholeWordBox.setText(Resource.getResourceString(\"WholeWord\"));\n        searchCriteriaPanel.add(wholeWordBox, GridBagConstraintsFactory.create(2, 1, GridBagConstraints.BOTH));\n\n        JLabel startDateLabel = new JLabel();\n        ResourceHelper.setText(startDateLabel, \"StartDate\");\n        searchCriteriaPanel.add(startDateLabel, GridBagConstraintsFactory.create(3, 0, GridBagConstraints.HORIZONTAL));\n\n        JLabel endDateLabel = new JLabel();\n        ResourceHelper.setText(endDateLabel, \"EndDate\");\n        searchCriteriaPanel.add(endDateLabel, GridBagConstraintsFactory.create(3, 1, GridBagConstraints.HORIZONTAL));\n\n        startDateChooser = new JDateChooser(new PlainDateEditor());\n        GridBagConstraints gbc1 = GridBagConstraintsFactory.create(4, 0, GridBagConstraints.NONE, 0.0, 0.0);\n        gbc1.anchor = GridBagConstraints.WEST;\n        searchCriteriaPanel.add(startDateChooser, gbc1);\n\n        endDateChooser = new JDateChooser(new PlainDateEditor());\n        GridBagConstraints gbc2 = GridBagConstraintsFactory.create(4, 1, GridBagConstraints.NONE, 0.0, 0.0);\n        gbc2.anchor = GridBagConstraints.WEST;\n        searchCriteriaPanel.add(endDateChooser, gbc2);\n\n        GridBagConstraints gridBagConstraints25 = GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH, 0.0,\n                1.0);\n        gridBagConstraints25.gridwidth = 5;\n        searchCriteriaPanel.add(createCheckBoxPanel(), gridBagConstraints25);\n\n        return searchCriteriaPanel;\n    }\n\n    @Override\n    public JComponent getComponent() {\n\n        if (!isInitialized) {\n            CategoryModel.getReference().addListener(this);\n\n            // init the UI components\n            initComponents();\n\n            for (Model m : Model.getExistingModels()) {\n                if (m instanceof Searchable)\n                    addModel(m);\n            }\n            // show the search results as a 2 column sortable table\n            // showing the appt date and text\n            resultsTable.setModel(new TableSorter(\n                    new String[]{Resource.getResourceString(\"Item\"), Resource.getResourceString(\"Type\"),\n                            Resource.getResourceString(\"Date\"), \"key\", \"class\", \"color\"},\n                    new Class[]{String.class, String.class, Date.class, Integer.class, Class.class, String.class}));\n\n            // hide columns with the key, class\n            TableColumnModel colModel = resultsTable.getColumnModel();\n            TableColumn col = colModel.getColumn(3);\n            resultsTable.removeColumn(col);\n            col = colModel.getColumn(3);\n            resultsTable.removeColumn(col);\n            col = colModel.getColumn(3);\n            resultsTable.removeColumn(col);\n\n            loadCategories();\n            isInitialized = true;\n        }\n        return this;\n    }\n\n    /**\n     * fill in the search criteria from the UI settings\n     *\n     * @return the criteria\n     */\n    private SearchCriteria getCriteria() {\n        SearchCriteria criteria = new SearchCriteria();\n        criteria.setSearchString(searchText.getText());\n        criteria.setCaseSensitive(caseSensitiveCheckBox.isSelected());\n        criteria.setCategory((String) categoryComboBox.getSelectedItem());\n        criteria.setWholeWord(wholeWordBox.isSelected());\n        Calendar cal = startDateChooser.getCalendar();\n        if (cal != null) {\n            cal.set(Calendar.HOUR_OF_DAY, 0);\n            cal.set(Calendar.MINUTE, 0);\n            cal.set(Calendar.SECOND, 0);\n            cal.add(Calendar.SECOND, -1);\n            criteria.setStartDate(cal.getTime());\n        }\n        cal = endDateChooser.getCalendar();\n        if (cal != null) {\n            cal.set(Calendar.HOUR_OF_DAY, 23);\n            cal.set(Calendar.MINUTE, 59);\n            cal.set(Calendar.SECOND, 0);\n            cal.add(Calendar.SECOND, -1);\n            criteria.setEndDate(cal.getTime());\n        }\n        criteria.setHoliday(holidayCheckBox.isSelected());\n        criteria.setRepeating(repeatCheckBox.isSelected());\n        criteria.setTodo(todoCheckBox.isSelected());\n        criteria.setVacation(vacationCheckBox.isSelected());\n        criteria.setHasLinks(linkCheckBox.isSelected());\n        return criteria;\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see net.sf.borg.ui.DockableView#getFrameTitle()\n     */\n    @Override\n    public String getFrameTitle() {\n        return Resource.getResourceString(\"srch\");\n    }\n\n    @Override\n    public String getModuleName() {\n        return Resource.getResourceString(\"srch\");\n    }\n\n    @Override\n    public ViewType getViewType() {\n        return ViewType.SEARCH;\n    }\n\n    /**\n     * Inits the components.\n     */\n    private void initComponents() {\n\n        setLayout(new GridBagLayout());\n\n        add(createSearchCriteriaPanel(), GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\n        JPanel searchButtonPanel = new JPanel();\n        GridLayout gridLayout18 = new GridLayout();\n        searchButtonPanel.setLayout(gridLayout18);\n        gridLayout18.setRows(1);\n        gridLayout18.setHgap(5);\n\n        JButton searchButton = new JButton();\n        ResourceHelper.setText(searchButton, \"srch\");\n        searchButton.setIcon(IconHelper.getIcon(\"/resource/Find16.gif\"));\n        searchButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n                SearchCriteria criteria = getCriteria();\n\n                if (criteria.getStartDate() != null && criteria.getEndDate() != null\n                        && criteria.getStartDate().after(criteria.getEndDate())) {\n                    Errmsg.getErrorHandler().notice(Resource.getResourceString(\"Start_After_End\"));\n                    return;\n                }\n                refresh();\n                TableSorter tm = (TableSorter) resultsTable.getModel();\n                if (tm.getRowCount() == 0)\n                    Errmsg.getErrorHandler().notice(Resource.getResourceString(\"no_search_results\"));\n            }\n\n        });\n        searchButtonPanel.add(searchButton, null);\n\n        add(searchButtonPanel, GridBagConstraintsFactory.create(0, 2, GridBagConstraints.NONE));\n\n        add(createResultsPanel(), GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH, 1.0, 1.0));\n\n        // set user color renderer\n        TableCellRenderer defaultObjectRenderer = resultsTable\n                .getDefaultRenderer(Object.class);\n        resultsTable.setDefaultRenderer(Object.class,\n                new SearchTableCellRenderer(defaultObjectRenderer));\n        TableCellRenderer defaultDateRenderer = resultsTable\n                .getDefaultRenderer(Date.class);\n        resultsTable.setDefaultRenderer(Date.class, new SearchTableCellRenderer(\n                defaultDateRenderer));\n        TableCellRenderer defaultIntegerRenderer = resultsTable\n                .getDefaultRenderer(Integer.class);\n        resultsTable.setDefaultRenderer(Integer.class,\n                new SearchTableCellRenderer(defaultIntegerRenderer));\n\n    }\n\n    @Override\n    public void initialize(MultiView parent) {\n        final MultiView par = parent;\n        parent.addToolBarItem(IconHelper.getIcon(\"/resource/Find16.gif\"), getModuleName(),\n                new ActionListener() {\n                    @Override\n                    public void actionPerformed(ActionEvent evt) {\n                        par.setView(ViewType.SEARCH);\n                    }\n                });\n        TrayIconProxy.addAction(getModuleName(), new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent evt) {\n                par.setView(ViewType.SEARCH);\n            }\n        });\n    }\n\n    @Override\n    public void print() {\n        try {\n            TablePrinter.printTable(resultsTable);\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n    }\n\n    @Override\n    public void update(ChangeEvent event) {\n\n        // handle category change event\n        if (event.getModel() instanceof CategoryModel) {\n            loadCategories();\n            return;\n        }\n        refresh();\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see net.sf.borg.ui.View#refresh()\n     */\n    @Override\n    public void refresh() {\n\n        SearchCriteria criteria = getCriteria();\n\n        // empty the table\n        TableSorter tm = (TableSorter) resultsTable.getModel();\n        tm.addMouseListenerToHeaderInTable(resultsTable);\n        tm.setRowCount(0);\n\n        // load the search results into the table, filtering the list by the\n        // search criteria\n\n        if (apptCheckBox.isSelected()) {\n            Collection<Appointment> appointments = AppointmentModel.getReference().search(criteria);\n            for (Appointment appt : appointments) {\n                Object[] ro = new Object[6];\n\n                try {\n                    ro[0] = appt.getText().replace('\\n', ' ');\n                    ro[1] = Resource.getResourceString(\"appointment\");\n                    ro[2] = appt.getDate();\n                    ro[3] = Integer.valueOf(appt.getKey());\n                    ro[4] = Appointment.class;\n                    ro[5] = appt.getColor();\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n\n            }\n            \n            Collection<LabelEntity> labels = SubscribedCalendars.getReference().search(criteria);\n            for( LabelEntity label : labels ) {\n            \tObject[] ro = new Object[6];\n\n                try {\n                    ro[0] = label.getText().replace('\\n', ' ');\n                    ro[1] = \"Subscribed Event\";\n                    ro[2] = label.getDate();\n                    ro[3] = 0;\n                    ro[4] = LabelEntity.class;\n                    ro[5] = label.getColor();\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n            }\n        }\n\n        if (addressCheckBox.isSelected()) {\n\n            Collection<Address> addresses = AddressModel.getReference().search(criteria);\n            for (Address addr : addresses) {\n                Object[] ro = new Object[6];\n\n                try {\n                    ro[0] = ((addr.getFirstName() == null) ? \"\" : (addr.getFirstName() + \" \"))\n                            + ((addr.getLastName() == null) ? \"\" : addr.getLastName());\n                    ro[1] = Resource.getResourceString(\"Address\");\n                    ro[2] = addr.getBirthday();\n                    ro[3] = Integer.valueOf(addr.getKey());\n                    ro[4] = Address.class;\n                    ro[5] = \"black\";\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n\n            }\n        }\n\n        Collection<KeyedEntity<?>> taskItems = TaskModel.getReference().search(criteria);\n        for (KeyedEntity<?> item : taskItems) {\n            Object[] ro = new Object[6];\n\n            try {\n                if (item instanceof Project && projectCheckBox.isSelected()) {\n                    ro[0] = ((Project) item).getDescription();\n                    ro[1] = Resource.getResourceString(\"project\");\n                    ro[2] = ((Project) item).getDueDate();\n                    ro[3] = Integer.valueOf(item.getKey());\n                    ro[4] = Project.class;\n                    ro[5] = ((Project) item).getColor();\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } else if (item instanceof Task && taskCheckBox.isSelected()) {\n                    ro[0] = ((Task) item).getSummary();\n                    ro[1] = Resource.getResourceString(\"task\");\n                    ro[2] = ((Task) item).getDueDate();\n                    ro[3] = Integer.valueOf(item.getKey());\n                    ro[4] = Task.class;\n                    ro[5] = ((Task) item).getColor();\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } else if (item instanceof Subtask && taskCheckBox.isSelected()) {\n                    ro[0] = ((Subtask) item).getDescription();\n                    ro[1] = Resource.getResourceString(\"subtask\");\n                    ro[2] = ((Subtask) item).getDueDate();\n                    ro[3] = Integer.valueOf(item.getKey());\n                    ro[4] = Subtask.class;\n                    ro[5] = ((Subtask) item).getColor();\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                }\n\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().errmsg(e);\n                return;\n            }\n\n        }\n\n        if (memoCheckBox.isSelected()) {\n            Collection<Memo> memos = MemoModel.getReference().search(criteria);\n            for (Memo memo : memos) {\n                Object[] ro = new Object[6];\n\n                try {\n                    ro[0] = memo.getMemoName();\n                    ro[1] = Resource.getResourceString(\"memo\");\n                    ro[2] = null;\n                    ro[3] = Integer.valueOf(0);\n                    ro[4] = Memo.class;\n                    ro[5] = \"black\";\n                    tm.addRow(ro);\n                    tm.tableChanged(new TableModelEvent(tm));\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n\n            }\n        }\n\n        // sort the table by date\n        tm.sortByColumn(0);\n    }\n\n    /**\n     * Show the selected item\n     *\n     * @throws Exception\n     */\n    private void showSelectedItem() throws Exception {\n\n        // get the selected row\n        int[] rows = resultsTable.getSelectedRows();\n        if (rows.length != 1)\n            return;\n        int row = rows[0];\n        TableSorter tm = (TableSorter) resultsTable.getModel();\n\n        int key = ((Integer) tm.getValueAt(row, 3)).intValue();\n        Class<?> cl = (Class<?>) tm.getValueAt(row, 4);\n        if (cl == Appointment.class) {\n            Appointment ap = AppointmentModel.getReference().getAppt(key);\n            if (ap == null) {\n                return;\n            }\n\n            Calendar cal = new GregorianCalendar();\n            cal.setTime(ap.getDate());\n\n            // bring up an appt editor window\n            AppointmentListView ag = new AppointmentListView(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n                    cal.get(Calendar.DATE));\n            ag.showApp(ap.getKey());\n            ag.showView();\n        }\n        // open a project\n        else if (cl == Project.class) {\n            Project ap = TaskModel.getReference().getProject(key);\n            if (ap == null) {\n                return;\n            }\n\n            new ProjectView(ap, ProjectView.Action.CHANGE, null).showView();\n        }\n        // open a task\n        else if (cl == Task.class) {\n            Task ap = TaskModel.getReference().getTask(key);\n            if (ap == null) {\n                return;\n            }\n\n            new TaskView(ap, TaskView.Action.CHANGE, null).showView();\n        }\n        // open an address\n        else if (cl == Address.class) {\n            Address ap = AddressModel.getReference().getAddress(key);\n            if (ap == null) {\n                return;\n            }\n            new AddressView(ap).showView();\n        }\n        // open a memo\n        else if (cl == Memo.class) {\n            Component c = MultiView.getMainView().setView(ViewType.MEMO);\n\n            // show the actual memo\n            if (c != null && c instanceof MemoPanel) {\n                MemoPanel mp = (MemoPanel) c;\n                mp.selectMemo((String) tm.getValueAt(row, 0));\n            }\n        }\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    public void exportToCSV(Writer writer) throws IOException {\n\n        try {\n\n            TableSorter ts = (TableSorter) resultsTable.getModel();\n            TableColumnModel cm = resultsTable.getColumnModel();\n            StringBuffer header = new StringBuffer();\n            for (int j = 0; j < cm.getColumnCount(); j++) {\n                header.append(esc((String) cm.getColumn(j).getHeaderValue()));\n                if (j != cm.getColumnCount())\n                    header.append(\", \");\n            }\n            writer.write(header + \"\\r\\n\");\n\n            for (int i = 0; i < ts.getRowCount(); i++) {\n                StringBuffer buffer = new StringBuffer();\n                for (int j = 0; j < cm.getColumnCount(); j++) {\n                    int cidx = cm.getColumn(j).getModelIndex();\n                    Object o = ts.getValueAt(i, cidx);\n                    if (o instanceof Date) {\n                        buffer.append(esc(DateFormat.getDateTimeInstance().format((Date) o)));\n                    } else if (o instanceof Number) {\n                        buffer.append(o);\n                    } else if (o instanceof Class) {\n                        buffer.append(((Class) o).getTypeName());\n                    } else\n                        buffer.append(esc((String) o));\n                    if (j != cm.getColumnCount())\n                        buffer.append(\", \");\n                }\n                writer.write(buffer + \"\\r\\n\");\n            }\n        } finally {\n            writer.close();\n        }\n    }\n\n    private String esc(String s) {\n        if (s == null) return \"\";\n        return s.replace(\",\", \" \");\n    }\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/SqlRunner.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.ScrolledDialog;\nimport net.sf.borg.ui.util.TableSorter;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.sql.ResultSet;\nimport java.util.logging.Logger;\n\n/**\n * SqlRunner is a UI that lets a user run SQL against the database. It presents\n * the results (if any) in a read-only table. It is mainly for debugging. the\n * average user would never use this.\n * \n */\n\nclass SqlRunner extends JFrame {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate JEditorPane editor;\n\n\tpublic SqlRunner() {\n\n\t\tsuper();\n\n\t\t// init the gui components\n\t\tinitComponents();\n\n\t\tthis.setTitle(Resource.getResourceString(\"RunSQL\"));\n\n\t\tpack();\n\n\t}\n\n\t/**\n\t * initialize the ui - a simple editor with buttons to run the sql or clear\n\t * the sql\n\t */\n\tprivate void initComponents() {\n\t\tthis.getContentPane().setLayout(new GridBagLayout());\n\n\t\tthis.editor = new JEditorPane();\n\t\tJPanel jPanel1 = new JPanel();\n\t\tJButton runButton = new JButton();\n\n\t\tthis.editor.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(\n\t\t\t\t0, 0, 0)));\n\n\t\tJScrollPane jScrollPane1 = new JScrollPane();\n\t\tjScrollPane1.setPreferredSize(new java.awt.Dimension(554, 404));\n\t\tjScrollPane1.setViewportView(this.editor);\n\n\t\trunButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Forward16.gif\"));\n\n\t\trunButton.setText(\"Run\");\n\t\trunButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\trunbuttonActionPerformed();\n\t\t\t}\n\t\t});\n\n\t\tjPanel1.add(runButton);\n\n\t\tJButton clearButton = new JButton();\n\t\tclearButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Undo16.gif\"));\n\t\tclearButton.setText(\"Clear\");\n\t\tclearButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tSqlRunner.this.editor.setText(\"\");\n\t\t\t}\n\t\t});\n\n\t\tthis.getContentPane().add(\n\t\t\t\tjScrollPane1,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH,\n\t\t\t\t\t\t1.0, 1.0));\n\t\tjPanel1.add(clearButton, clearButton.getName());\n\t\tthis.getContentPane()\n\t\t\t\t.add(jPanel1,\n\t\t\t\t\t\tGridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t}\n\n\t/**\n\t * run the SQL\n\t */\n\tprivate void runbuttonActionPerformed() {\n\t\ttry {\n\n\t\t\tDBHelper.getController().beginTransaction();\n\n\t\t\t// run the sql\n\t\t\tResultSet r = DBHelper.getController().execQuery(this.editor.getText());\n\t\t\tDBHelper.getController().commitTransaction();\n\n\t\t\t// display the results in a table\n\t\t\tif (r != null && r.next()) {\n\t\t\t\tJTable tbl = new JTable();\n\t\t\t\ttbl.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\n\n\t\t\t\tint cols = r.getMetaData().getColumnCount();\n\t\t\t\tString[] colnames = new String[cols];\n\t\t\t\tClass<?>[] classes = new Class[cols];\n\t\t\t\tfor (int c = 0; c < cols; c++) {\n\t\t\t\t\tcolnames[c] = r.getMetaData().getColumnName(c + 1);\n\t\t\t\t\tclasses[c] = String.class;\n\t\t\t\t}\n\t\t\t\tTableSorter ts = new TableSorter(colnames, classes);\n\t\t\t\tts.addMouseListenerToHeaderInTable(tbl);\n\t\t\t\ttbl.setModel(ts);\n\t\t\t\tObject[] row = new Object[cols];\n\t\t\t\tfor (; !r.isAfterLast(); r.next()) {\n\t\t\t\t\tfor (int i = 1; i <= cols; i++) {\n\t\t\t\t\t\trow[i - 1] = r.getString(i);\n\t\t\t\t\t}\n\t\t\t\t\tts.addRow(row);\n\t\t\t\t}\n\n\t\t\t\t// use a ScrolledDialog to display the table\n\t\t\t\tScrolledDialog.showTable(tbl);\n\t\t\t} else\n\t\t\t\tScrolledDialog.showNotice(Resource\n\t\t\t\t\t\t.getResourceString(\"noOutput\"));\n\t\t\tif (r != null)\n\t\t\t\tr.close();\n\n\t\t} catch (Exception e) {\n\t\t\tlog.severe(e.toString());\n\t\t\ttry {\n\t\t\t\tDBHelper.getController().rollbackTransaction();\n\t\t\t} catch (Exception e2) {\n\t\t\t\t// empty\n\t\t\t}\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\t// since the SQL may affect any of the tables, we need to\n\t\t// just tell all models to refresh\n\t\tModel.syncModels();\n\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/SunTrayIconProxy.java",
    "content": "/*\n * This file is part of BORG.\n * \n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n * \n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n * \n * Copyright 2005 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport java.awt.AWTException;\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.FontMetrics;\nimport java.awt.Graphics2D;\nimport java.awt.Image;\nimport java.awt.Menu;\nimport java.awt.MenuItem;\nimport java.awt.PopupMenu;\nimport java.awt.RenderingHints;\nimport java.awt.SystemTray;\nimport java.awt.Toolkit;\nimport java.awt.TrayIcon;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.image.BufferedImage;\nimport java.text.DateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.logging.Logger;\n\nimport javax.swing.SwingUtilities;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.sync.google.GDrive;\nimport net.sf.borg.ui.TrayIconProxy.TrayIconProxyI;\nimport net.sf.borg.ui.options.OptionsView;\nimport net.sf.borg.ui.popup.ReminderManager;\n\n/** communicates with the new java built-in system tray APIs */\npublic class SunTrayIconProxy implements Prefs.Listener, TrayIconProxyI {\n\n\t// action that opens the main view\n\tstatic private class OpenListener implements ActionListener {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\tUIControl.toFront();\n\t\t}\n\t}\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tprivate int iconSize = 16;\n\n\tprivate final Menu actionMenu = new Menu();\n\n\t/** the TrayIcon */\n\tprivate TrayIcon trayIcon = null;\n\tprivate TrayIcon syncIcon = null;\n\tprivate TrayIcon uploadIcon = null; \n\n\t/**\n\t * initalize the system tray\n\t *\n\t * @throws Exception\n\t */\n\tpublic void init() throws Exception {\n\n\t\tif (!SystemTray.isSupported())\n\t\t\tthrow new Exception(\"Systray not supported\");\n\n\t\ticonSize = SystemTray.getSystemTray().getTrayIconSize().height;\n\n\t\ttrayIcon = new TrayIcon(Toolkit.getDefaultToolkit().getImage(getClass().getResource(\"/resource/borg16.jpg\")));\n\n\t\ttrayIcon.setToolTip(\"BORG\");\n\n\t\tsyncIcon = new TrayIcon(\n\t\t\t\tToolkit.getDefaultToolkit().getImage(getClass().getResource(\"/resource/Refresh16.gif\")));\n\n\t\tactionMenu.setLabel(Resource.getResourceString(\"Open\"));\n\n\t\tPopupMenu popup = new PopupMenu();\n\n\t\tString fontName = Prefs.getPref(PrefName.DEFFONT);\n\t\tif (!fontName.isEmpty()) {\n\t\t\tFont f = Font.decode(fontName);\n\t\t\tpopup.setFont(f);\n\t\t}\n\n\t\tMenuItem item = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Open_Calendar\"));\n\t\titem.addActionListener(new OpenListener());\n\t\tpopup.add(item);\n\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Show_Pops\"));\n\n\t\titem.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tReminderManager rm = ReminderManager.getReminderManager();\n\t\t\t\tif (rm != null)\n\t\t\t\t\trm.showAll();\n\t\t\t}\n\n\t\t});\n\t\tpopup.add(item);\n\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Hide_Pops\"));\n\n\t\titem.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tReminderManager rm = ReminderManager.getReminderManager();\n\t\t\t\tif (rm != null)\n\t\t\t\t\trm.hideAll();\n\t\t\t}\n\n\t\t});\n\t\tpopup.add(item);\n\n\t\tpopup.add(actionMenu);\n\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Options\"));\n\t\titem.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tOptionsView.getReference().setVisible(true);\n\t\t\t}\n\t\t});\n\t\tpopup.add(item);\n\n\t\tpopup.addSeparator();\n\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"About\"));\n\t\titem.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tMainMenu.AboutMIActionPerformed();\n\t\t\t}\n\t\t});\n\t\tpopup.add(item);\n\n\t\tpopup.addSeparator();\n\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Exit\"));\n\t\titem.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tUIControl.shutDownUI();\n\t\t\t}\n\t\t});\n\t\tpopup.add(item);\n\n\t\ttrayIcon.setPopupMenu(popup);\n\t\ttrayIcon.addActionListener(new OpenListener());\n\n\t\tSystemTray tray = SystemTray.getSystemTray();\n\t\ttray.add(trayIcon);\n\n\t\tuploadIcon = new TrayIcon(\n\t\t\t\tToolkit.getDefaultToolkit().getImage(getClass().getResource(\"/resource/Up16.gif\")));\n\n\t\tPopupMenu uploadMenu = new PopupMenu();\n\t\tMenuItem uploadItem = new MenuItem();\n\t\tuploadItem.setLabel(\"Upload Database\");\n\t\tuploadItem.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t// Trigger upload action\n\t\t\t\ttry {\n\t\t\t\t\tGDrive.getReference().vacuumAndUpload();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tuploadMenu.add(uploadItem);\n\t\tuploadIcon.setPopupMenu(uploadMenu);\n\t\tPopupMenu menu = new PopupMenu();\n\t\tMenuItem sitem = new MenuItem();\n\t\tsitem.setLabel(Resource.getResourceString(\"Sync\"));\n\t\titem = new MenuItem();\n\t\titem.setLabel(Resource.getResourceString(\"Sync\"));\n\t\titem.addActionListener(SyncModule.syncButtonListener);\n\t\tmenu.add(item);\n\t\tif (syncIcon != null)\n\t\t\tsyncIcon.setPopupMenu(menu);\n\n\t\tupdateImage();\n\n\t\tPrefs.addListener(this);\n\n\t\tstartRefreshTimer();\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\t\tupdateImage();\n\n\t}\n\n\t/**\n\t * set the icon image to the current date or a fixed icon with a B on it\n\t * depending on user preference.\n\t */\n\tpublic void updateImage() {\n\t\tImage image = null;\n\t\tif (Prefs.getBoolPref(PrefName.SYSTRAYDATE)) {\n\n\t\t\tlog.fine(\"Updating systray image...\");\n\n\t\t\t// get date text\n\t\t\tString text = Integer.toString(new GregorianCalendar().get(Calendar.DATE));\n\n\t\t\tBufferedImage bimage = new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_RGB);\n\t\t\tGraphics2D g = bimage.createGraphics();\n\t\t\tg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);\n\n\t\t\t// draw icon background\n\t\t\tTheme t = Theme.getCurrentTheme();\n\t\t\tg.setColor(new Color(t.getTrayIconBg()));\n\t\t\tg.fillRect(0, 0, iconSize, iconSize);\n\n\t\t\t// draw date centered\n\t\t\tFont font = Font.decode(Prefs.getPref(PrefName.TRAYFONT));\n\t\t\tg.setFont(font);\n\t\t\tFontMetrics metrics = g.getFontMetrics();\n\t\t\tg.setColor(new Color(t.getTrayIconFg()));\n\t\t\tg.drawString(text, (iconSize - metrics.stringWidth(text)) / 2, (iconSize + metrics.getAscent()) / 2);\n\t\t\tg.dispose();\n\n\t\t\timage = bimage;\n\n\t\t\ttrayIcon.setToolTip(\"BORG - \" + DateFormat.getDateInstance(DateFormat.MEDIUM).format(new Date()));\n\n\t\t} else {\n\t\t\timage = Toolkit.getDefaultToolkit().getImage(getClass().getResource(\"/resource/borg16.jpg\"));\n\n\t\t}\n\n\t\ttrayIcon.setImage(image);\n\n\t}\n\n\t/** start a timer that updates the date icon */\n\tprivate void startRefreshTimer() {\n\n\t\tTimer updatetimer = new Timer(\"SysTrayTimer\");\n\t\tupdatetimer.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tupdateImage();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}, 60 * 1000, 15 * 60 * 1000);\n\n\t}\n\n\t/**\n\t * Add an action to the action menu\n\t * \n\t * @param text   the text for the menu item\n\t * @param action the action listener for the menu item\n\t */\n\tpublic void addAction(String text, ActionListener action) {\n\t\tMenuItem item = new MenuItem();\n\t\titem.setLabel(text);\n\t\titem.addActionListener(action);\n\n\t\tactionMenu.add(item);\n\t}\n\n\t@Override\n\tpublic void enableTrayIcon() {\n\t\ttry {\n\t\t\tSystemTray.getSystemTray().add(syncIcon);\n\t\t} catch (AWTException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void disableTrayIcon() {\n\t\tSystemTray.getSystemTray().remove(syncIcon);\n\n\t}\n\t\n\t@Override\n\tpublic void enableUploadTrayIcon() {\n\t\ttry {\n\t\t\tSystemTray.getSystemTray().add(uploadIcon);\n\t\t} catch (AWTException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void disableUploadTrayIcon() {\n\t\tSystemTray.getSystemTray().remove(uploadIcon);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/SyncModule.java",
    "content": "package net.sf.borg.ui;\n\nimport java.awt.Component;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.SwingWorker;\nimport javax.swing.filechooser.FileNameExtensionFilter;\n\nimport net.fortuna.ical4j.vcard.VCard;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.IOHelper;\nimport net.sf.borg.common.LogViewer;\nimport net.sf.borg.common.ModalMessageServer;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.sync.SyncLog;\nimport net.sf.borg.model.sync.google.GCal;\nimport net.sf.borg.model.sync.google.GDrive;\nimport net.sf.borg.model.sync.ical.CardDav;\nimport net.sf.borg.model.sync.ical.ICal;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.options.GoogleOptionsPanel;\nimport net.sf.borg.ui.options.IcalOptionsPanel;\nimport net.sf.borg.ui.options.OptionsView;\nimport net.sf.borg.ui.util.FileDrop;\nimport net.sf.borg.ui.util.IconHelper;\n\npublic class SyncModule implements Module, Prefs.Listener, Model.Listener {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\tprivate static final PrefName url_pref = new PrefName(\"saved_import_url\", \"\");\n\tpublic static final ActionListener syncButtonListener = new ActionListener() {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\ttry {\n\n\t\t\t\tif (GCal.isSyncing()) {\n\t\t\t\t\trunGcalSync(false, false);\n\t\t\t\t} else {\n\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t};\n\t\n\tprivate JButton syncToolbarButton = null;\n\n\tpublic SyncModule() {\n\n\t}\n\n\tpublic static JMenu getIcalMenu() {\n\t\tJMenu m = new JMenu();\n\t\tm.setText(\"Sync\");\n\n\t\tJMenu icsmenu = new JMenu(\"ICS\");\n\n\t\tJMenuItem imp = new JMenuItem();\n\t\timp.setText(Resource.getResourceString(\"Import\"));\n\t\timp.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\t// prompt for a file\n\t\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(Resource.getResourceString(\"ical_files\"),\n\t\t\t\t\t\t\"ics\", \"ICS\", \"ical\", \"ICAL\", \"icalendar\");\n\t\t\t\tchooser.setFileFilter(filter);\n\t\t\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\treturn;\n\n\t\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\n\t\t\t\ttry {\n\n\t\t\t\t\tString warning = ICal.importIcalFromFile(s);\n\t\t\t\t\tif (warning != null && !warning.isEmpty())\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(warning);\n\t\t\t\t\tCategoryModel.syncModels();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\n\t\ticsmenu.add(imp);\n\n\t\tJMenuItem impUrl = new JMenuItem();\n\t\timpUrl.setText(Resource.getResourceString(\"ImportUrl\"));\n\t\timpUrl.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\t// prompt for a file\n\t\t\t\tString urlString = JOptionPane.showInputDialog(null, Resource.getResourceString(\"enturl\"),\n\t\t\t\t\t\tPrefs.getPref(url_pref));\n\t\t\t\tif (urlString == null)\n\t\t\t\t\treturn;\n\n\t\t\t\tPrefs.putPref(url_pref, urlString);\n\t\t\t\ttry {\n\n\t\t\t\t\tString warning = ICal.importIcalFromUrl(urlString);\n\t\t\t\t\tif (warning != null && !warning.isEmpty())\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(warning);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\n\t\ticsmenu.add(impUrl);\n\n\t\tJMenuItem exp = new JMenuItem();\n\t\texp.setText(Resource.getResourceString(\"exportToFile\"));\n\t\texp.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\texport(Prefs.getIntPref(PrefName.ICAL_EXPORTYEARS));\n\t\t\t}\n\t\t});\n\n\t\ticsmenu.add(exp);\n\n\t\tm.add(icsmenu);\n\t\t\n\t\tJMenuItem expz = new JMenuItem();\n\t\texpz.setText(Resource.getResourceString(\"exportToIcalZipFile\"));\n\t\texpz.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\texportToZip();\n\t\t\t}\n\t\t});\n\n\t\ticsmenu.add(expz);\n\n\t\tm.add(icsmenu);\n\n\t\tJMenu vcardmenu = new JMenu(\"VCARD\");\n\n\t\tJMenuItem impvc = new JMenuItem();\n\t\timpvc.setText(Resource.getResourceString(\"ImportFile\"));\n\t\timpvc.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\t// prompt for a file\n\t\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(\"VCARD\", \"vcs\", \"VCS\", \"vcf\", \"VCF\",\n\t\t\t\t\t\t\"vcard\", \"VCARD\");\n\t\t\t\tchooser.setFileFilter(filter);\n\t\t\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\treturn;\n\n\t\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\n\t\t\t\ttry {\n\n\t\t\t\t\tList<VCard> vcards = CardDav.importVcardFromFile(s);\n\t\t\t\t\tString warning = CardDav.importVCard(vcards);\n\t\t\t\t\tif (warning != null && !warning.isEmpty())\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(warning);\n\t\t\t\t\tCategoryModel.syncModels();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\n\t\tvcardmenu.add(impvc);\n\t\t\n\t\tJMenuItem expvc = new JMenuItem();\n\t\texpvc.setText(Resource.getResourceString(\"exportToFile\"));\n\t\texpvc.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\texportVcard();\n\t\t\t}\n\t\t});\n\n\t\tvcardmenu.add(expvc);\n\n\t\tm.add(vcardmenu);\n\n\t\tJMenu gcalmenu = new JMenu(\"Google\");\n\t\tJMenuItem gcalsyncmi = new JMenuItem(\"Sync\");\n\t\tgcalsyncmi.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!GCal.isSyncing()) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\trunGcalSync(false, false);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tgcalmenu.add(gcalsyncmi);\n\t\tJMenuItem gcalsyncCleanmi = new JMenuItem(Resource.getResourceString(\"Sync_Cleanup\"));\n\t\tgcalsyncCleanmi.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!GCal.isSyncing()) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\trunGcalSync(false, true);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tgcalmenu.add(gcalsyncCleanmi);\n\n\t\tJMenuItem gcalow = new JMenuItem(\"Full Sync\");\n\t\tgcalow.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!GCal.isSyncing()) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\trunGcalSync(true, false);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tgcalmenu.add(gcalow);\n\t\t\n\t\tJMenuItem guns = new JMenuItem(\"UnSync\");\n\t\tguns.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!GCal.isSyncing()) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null, Resource.getResourceString(\"unsync-Warn\"),\n\t\t\t\t\t\t\t\"WARNING\", JOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\tJOptionPane.WARNING_MESSAGE);\n\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tGCal.getReference().unsync();\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tgcalmenu.add(guns);\n\t\t\n\t\tJMenuItem forceDlMI = new JMenuItem(\"Force DB Download on Restart\");\n\t\tforceDlMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!GCal.isSyncing()) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource.getResourceString(\"Sync-Not-Set\"), null,\n\t\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t \n\t\t\t\t\tPrefs.putPref(PrefName.GOOGLE_DB_FORCE_DOWNLOAD, \"true\");\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tgcalmenu.add(forceDlMI);\n\t\t\n\t\tJMenuItem uploadDBMI = new JMenuItem(\"Upload DB\");\n\t\tuploadDBMI.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\ttry {\n\t\t\t\t\tGDrive.getReference().vacuumAndUpload();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tgcalmenu.add(uploadDBMI);\n\n\n\n\t\tm.add(gcalmenu);\n\n\t\treturn m;\n\t}\n\n\tprivate static void runGcalSync(boolean fullsync, boolean cleanup) {\n\n\t\tfinal boolean full = fullsync;\n\t\tclass SyncWorker extends SwingWorker<Void, Object> {\n\t\t\t@Override\n\t\t\tpublic Void doInBackground() {\n\t\t\t\ttry {\n\n\t\t\t\t\tAppointmentModel.getReference().setNotifyListeners(false);\n\t\t\t\t\t// modally lock borg\n\t\t\t\t\tModalMessageServer.getReference().sendMessage(\"lock:\" + Resource.getResourceString(\"syncing\"));\n\t\t\t\t\tGCal.getReference().sync(Prefs.getIntPref(PrefName.GCAL_EXPORTYEARS), full, cleanup);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tlog.severe(\"***ERROR during sync***, please check logs\");\n\t\t\t\t\tModalMessageServer.getReference().sendLogMessage(e.getMessage());\n\t\t\t\t\tModalMessageServer.getReference().sendLogMessage(\"***ERROR during sync***, please check logs\");\n\t\t\t\t}\n\n\t\t\t\tModalMessageServer.getReference().sendLogMessage(Resource.getResourceString(\"done\"));\n\t\t\t\tAppointmentModel.getReference().setNotifyListeners(true);\n\t\t\t\tAppointmentModel.getReference().refresh();\n\n\n\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void done() {\n\t\t\t\tModalMessageServer.getReference().sendMessage(\"unlock\");\n\t\t\t\tnew LogViewer(GCal.getReference().getLogEntries()).setVisible(true);\n\n\t\t\t}\n\t\t}\n\n\t\t(new SyncWorker()).execute();\n\t}\n\n\n\t/**\n\t * export appts\n\t *\n\t * @param years - number of years to export or null\n\t */\n\tprivate static void export(Integer years) {\n\n\t\t// prompt for a file\n\t\tJFileChooser chooser = new JFileChooser();\n\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(Resource.getResourceString(\"ical_files\"), \"ics\",\n\t\t\t\t\"ICS\", \"ical\", \"ICAL\", \"icalendar\");\n\t\tchooser.setFileFilter(filter);\n\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\tint returnVal = chooser.showSaveDialog(null);\n\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\treturn;\n\n\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\n\t\t// auto append extension\n\t\tif (chooser.getFileFilter() != chooser.getAcceptAllFileFilter()) {\n\t\t\tif (!s.contains(\".\")) {\n\t\t\t\ts += \".ics\";\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tif (years != null) {\n\t\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\t\tcal.add(Calendar.YEAR, -1 * years.intValue());\n\t\t\t\tICal.exportIcalToFile(s, cal.getTime());\n\t\t\t} else {\n\t\t\t\tICal.exportIcalToFile(s, null);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\t\n\tprivate static void exportToZip() {\n\n\t\tString s;\n\t\twhile (true) {\n\t\t\t// prompt for a directory to store the files\n\t\t\tJFileChooser chooser = new JFileChooser();\n\n\t\t\tchooser.setCurrentDirectory(IOHelper.getHomeDirectory());\n\t\t\tchooser.setDialogTitle(Resource.getResourceString(\"select_export_dir\"));\n\t\t\tchooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\t\t\tchooser.setApproveButtonText(Resource.getResourceString(\"select_export_dir\"));\n\n\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\treturn;\n\n\t\t\ts = chooser.getSelectedFile().getAbsolutePath();\n\t\t\tIOHelper.setHomeDirectory(s);\n\t\t\tFile dir = new File(s);\n\t\t\tString err = null;\n\t\t\tif (!dir.exists()) {\n\t\t\t\terr = Resource.getResourceString(\"Directory_[\") + s + Resource.getResourceString(\"]_does_not_exist\");\n\t\t\t} else if (!dir.isDirectory()) {\n\t\t\t\terr = \"[\" + s + Resource.getResourceString(\"]_is_not_a_directory\");\n\t\t\t}\n\n\t\t\tif (err == null)\n\t\t\t\tbreak;\n\n\t\t\tErrmsg.getErrorHandler().notice(err);\n\t\t}\n\n\t\ttry {\n\t\t\tICal.exportApptsToFileByYear(s);\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\tprivate static void exportVcard() {\n\n\t\t// prompt for a file\n\t\tJFileChooser chooser = new JFileChooser();\n\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\tFileNameExtensionFilter filter = new FileNameExtensionFilter(\"VCARD\", \"vcs\", \"VCS\", \"VCF\", \"vcf\", \"vcard\",\n\t\t\t\t\"VCARD\");\n\t\tchooser.setFileFilter(filter);\n\t\tchooser.setDialogTitle(Resource.getResourceString(\"choose_file\"));\n\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\tint returnVal = chooser.showSaveDialog(null);\n\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\treturn;\n\n\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\n\t\t// auto append extension\n\t\tif (chooser.getFileFilter() != chooser.getAcceptAllFileFilter()) {\n\t\t\tif (!s.contains(\".\")) {\n\t\t\t\ts += \".vcf\";\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tCardDav.exportToFile(s);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Component getComponent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn \"SYNC\";\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\n\t\tPrefs.addListener(this);\n\t\tSyncLog.getReference().addListener(this);\n\n\t\tOptionsView.getReference().addPanel(new IcalOptionsPanel());\n\n\t\tOptionsView.getReference().addPanel(new GoogleOptionsPanel());\n\n\t\tnew FileDrop(parent, new FileDrop.Listener() {\n\t\t\t@Override\n\t\t\tpublic void filesDropped(java.io.File[] files) {\n\t\t\t\tfor (File f : files) {\n\t\t\t\t\tString warning;\n\t\t\t\t\ttry {\n\t\t\t\t\t\twarning = ICal.importIcalFromFile(f.getAbsolutePath());\n\t\t\t\t\t\tif (warning != null && !warning.isEmpty())\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().notice(warning);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tsyncToolbarButton = MultiView.getMainView().addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/Refresh16.gif\"),\n\t\t\t\tResource.getResourceString(\"Sync\"), syncButtonListener);\n\n\t\tString usetray = Prefs.getPref(PrefName.USESYSTRAY);\n\t\tif (usetray.equals(\"true\")) {\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\tupdateSyncButton();\n\t\t\t\tshowTrayIcon();\n\t\t\t} catch (Exception e1) {\n\t\t\t\te1.printStackTrace();\n\t\t\t}\n\t\t}\n\t\tSyncLog.getReference();\n\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\t\ttry {\n\t\t\tupdateSyncButton();\n\t\t\tshowTrayIcon();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tprivate void updateSyncButton() throws Exception {\n\n\t\tString label = Integer.toString(SyncLog.getReference().getAll().size());\n\n\t\tsyncToolbarButton.setText(label);\n\n\t}\n\n\tprivate void showTrayIcon() {\n\n\t\tif (TrayIconProxy.hasTrayIcon()) {\n\n\t\t\ttry {\n\t\t\t\tif (!(GCal.isSyncing()) || SyncLog.getReference().getAll().isEmpty()) {\n\t\t\t\t\tTrayIconProxy.disableTrayIcon();\n\t\t\t\t} else {\n\t\t\t\t\tTrayIconProxy.enableTrayIcon();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\ttry {\n\t\t\tupdateSyncButton();\n\t\t\tshowTrayIcon();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/TrayIconProxy.java",
    "content": "package net.sf.borg.ui;\n\nimport java.awt.event.ActionListener;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\n\npublic class TrayIconProxy {\n\n\tstatic private TrayIconProxyI singleton = null;\n\n\tpublic static TrayIconProxyI getReference() {\n\t\treturn singleton;\n\t}\n\n\t/**\n\t * Checks for presence of the tray icon.\n\t * \n\t * @return true, if the tray icon started up successfully, false otherwise\n\t */\n\tpublic static boolean hasTrayIcon() {\n\t\treturn trayIconStarted;\n\t}\n\n\t/* flag to indicate is a tray icon was started */\n\tprivate static boolean trayIconStarted = false;\n\n\tpublic static void startTrayIcon() {\n\n\t\t//boolean isWindows = System.getProperty(\"os.name\").startsWith(\"Windows\");\n\n\t\t// start the system tray icon - or at least attempt to\n\t\t// it doesn't run on all OSs and all WMs\n\t\ttrayIconStarted = true;\n\t\tString usetray = Prefs.getPref(PrefName.USESYSTRAY);\n\t\tif (!usetray.equals(\"true\")) {\n\t\t\ttrayIconStarted = false;\n\t\t} else {\n\t\t\ttry {\n\n\t\t\t\t\tsingleton = new SunTrayIconProxy();\n\t\t\t\t\n\t\t\t\tsingleton.init();\n\t\t\t} catch (UnsatisfiedLinkError le) {\n\t\t\t\tle.printStackTrace();\n\t\t\t\ttrayIconStarted = false;\n\t\t\t} catch (NoClassDefFoundError ncf) {\n\t\t\t\tncf.printStackTrace();\n\t\t\t\ttrayIconStarted = false;\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\ttrayIconStarted = false;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static void updateImage() {\n\n\t\tif (!trayIconStarted)\n\t\t\treturn;\n\n\t\tsingleton.updateImage();\n\n\t}\n\n\tpublic static void addAction(String text, ActionListener action) {\n\t\tif (!trayIconStarted)\n\t\t\treturn;\n\n\t\tsingleton.addAction(text, action);\n\t}\n\n\tpublic interface TrayIconProxyI {\n\n\t\tvoid init() throws Exception;\n\n\t\t/**\n\t\t * set the icon image to the current date or a fixed icon with a B on it\n\t\t * depending on user preference.\n\t\t */\n\t\tvoid updateImage();\n\n\t\t/**\n\t\t * Add an action to the action menu\n\t\t * \n\t\t * @param text   the text for the menu item\n\t\t * @param action the action listener for the menu item\n\t\t */\n\n\t\tvoid addAction(String text, ActionListener action);\n\t\t\n\t\tvoid enableTrayIcon();\n\t\t\n\t\tvoid disableTrayIcon();\n\n\t\tvoid enableUploadTrayIcon();\n\n\t\tvoid disableUploadTrayIcon();\n\n\t}\n\t\n\tpublic static void enableTrayIcon() {\n\t\tsingleton.enableTrayIcon();\n\t}\n\t\n\tpublic static void disableTrayIcon() {\n\t\tsingleton.disableTrayIcon();\n\t}\n\n\tpublic static void enableUploadTrayIcon() {\n\t\tsingleton.enableUploadTrayIcon();\n\n\t\t\n\t}\n\n\tpublic static void disableUploadTrayIcon() {\n\t\tsingleton.disableUploadTrayIcon();\n\t\t\n\t}\n\t\n\t\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/UIControl.java",
    "content": "package net.sf.borg.ui;\n\nimport java.awt.Dialog.ModalityType;\nimport java.awt.Font;\nimport java.awt.Frame;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.text.MessageFormat;\nimport java.util.Enumeration;\nimport java.util.logging.Logger;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JOptionPane;\nimport javax.swing.SwingWorker;\nimport javax.swing.Timer;\nimport javax.swing.UIManager;\nimport javax.swing.plaf.FontUIResource;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.control.Borg;\nimport net.sf.borg.model.ExportImport;\nimport net.sf.borg.model.db.DBHelper;\nimport net.sf.borg.model.sync.SyncLog;\nimport net.sf.borg.ui.address.AddrListView;\nimport net.sf.borg.ui.calendar.DayPanel;\nimport net.sf.borg.ui.calendar.MonthPanel;\nimport net.sf.borg.ui.calendar.TodoView;\nimport net.sf.borg.ui.calendar.WeekPanel;\nimport net.sf.borg.ui.calendar.YearPanel;\nimport net.sf.borg.ui.checklist.CheckListPanel;\nimport net.sf.borg.ui.memo.MemoPanel;\nimport net.sf.borg.ui.options.MiscellaneousOptionsPanel.SHUTDOWN_ACTION;\nimport net.sf.borg.ui.popup.ReminderListManager;\nimport net.sf.borg.ui.popup.ReminderManager;\nimport net.sf.borg.ui.popup.ReminderPopupManager;\nimport net.sf.borg.ui.task.TaskModule;\nimport net.sf.borg.ui.util.SplashScreen;\nimport net.sf.borg.ui.util.UIErrorHandler;\n\n/**\n * Class UIControl provides access to the UI from non-UI classes. UIControl\n * provides the main UI entry point.\n *\n *\n */\n\npublic class UIControl {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\t/**\n\t * splash screen\n\t */\n\tprivate static SplashScreen splashScreen = null;\n\n\tstatic public void setDefaultFont(Font f) {\n\t\tFontUIResource fui = new FontUIResource(f);\n\t\tEnumeration<Object> keys = UIManager.getDefaults().keys();\n\t\twhile (keys.hasMoreElements()) {\n\t\t\tObject key = keys.nextElement();\n\t\t\tObject value = UIManager.get(key);\n\t\t\tif (value instanceof FontUIResource)\n\t\t\t\tUIManager.put(key, fui);\n\t\t}\n\n\t}\n\n\t/**\n\t * Main UI initialization.\n\t *\n\t */\n\tpublic static void startUI() {\n\n\t\tErrmsg.setErrorHandler(new UIErrorHandler());\n\n\t\t// check database timestamp\n\t\ttry {\n\t\t\tDBHelper.checkTimestamp();\n\t\t} catch (Warning e1) {\n\t\t\tErrmsg.getErrorHandler().notice(e1.getMessage());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\t// default font\n\t\tString deffont = Prefs.getPref(PrefName.DEFFONT);\n\t\tif (!deffont.equals(\"\")) {\n\t\t\tFont f = Font.decode(deffont);\n\t\t\tsetDefaultFont(f);\n\t\t}\n\t\t// JGoodies Plastic L&F does not support the mac cmd key,\n\t\t// If the user is running OS X, go to default Java L&F instead\n\t\tString os = System.getProperty(\"os.name\").toLowerCase();\n\t\tString lnf = Prefs.getPref(PrefName.LNF);\n\n\t\tif (os.contains(\"mac\") && lnf.contains(\"jgoodies\")) {\n\t\t\t// Do nothing.\n\t\t} else {\n\t\t\t// set the look and feel\n\t\t\ttry {\n\n\t\t\t\t// jgoodies has been removed - was buggy\n\t\t\t\tif (lnf.contains(\"jgoodies\")) {\n\t\t\t\t\tlnf = \"com.formdev.flatlaf.FlatLightLaf\";\n\t\t\t\t\tPrefs.putPref(PrefName.LNF, lnf);\n\t\t\t\t}\n\n\t\t\t\tUIManager.setLookAndFeel(lnf);\n\t\t\t\tUIManager.getLookAndFeelDefaults().put(\"ClassLoader\", UIControl.class.getClassLoader());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.severe(e.toString());\n\t\t\t}\n\t\t}\n\n\t\t// pop up the splash if the option is set\n\t\tif (Prefs.getBoolPref(PrefName.SPLASH)) {\n\t\t\tsplashScreen = new SplashScreen(ModalityType.MODELESS);\n\t\t\tsplashScreen.setText(Resource.getResourceString(\"Initializing\"));\n\t\t\tsplashScreen.setVisible(true);\n\n\t\t\t/*\n\t\t\t * in order for the splash to be seen, we will complete initialization later (in\n\t\t\t * the swing thread).\n\t\t\t */\n\t\t\tTimer t = new Timer(3000, new ActionListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\t\tcompleteUIInitialization();\n\t\t\t\t}\n\t\t\t});\n\t\t\tt.setRepeats(false);\n\t\t\tt.start();\n\t\t} else\n\t\t\tcompleteUIInitialization();\n\n\t}\n\n\t/**\n\t * complete the parts of the UI initialization that run after the splash screen\n\t * has shown for a while\n\t *\n\t */\n\tprivate static void completeUIInitialization() {\n\n\t\t// tray icon\n\t\tTrayIconProxy.startTrayIcon();\n\n\t\t// create reminder manager\n\t\tif (Prefs.getBoolPref(PrefName.REMINDERLIST))\n\t\t\tReminderListManager.getReference();\n\t\telse\n\t\t\tReminderPopupManager.getReference();\n\n\t\t// create the main window\n\t\tMultiView mv = MultiView.getMainView();\n\n\t\t// load the UI modules into the main window\n\t\tmv.addModule(new DayPanel());\n\t\tmv.addModule(new WeekPanel());\n\t\tmv.addModule(new MonthPanel());\n\t\tmv.addModule(new YearPanel());\n\t\tmv.addModule(new AddrListView());\n\t\tmv.addModule(new TodoView());\n\t\tmv.addModule(new TaskModule());\n\t\tmv.addModule(new MemoPanel());\n\t\tmv.addModule(new CheckListPanel());\n\t\tmv.addModule(new SearchView());\n\t\tmv.addModule(new InfoView(\"/resource/RELEASE_NOTES.txt\", Resource.getResourceString(\"rlsnotes\")));\n\t\tmv.addModule(new InfoView(\"/resource/CHANGES.txt\", Resource.getResourceString(\"viewchglog\")));\n\t\tmv.addModule(new InfoView(\"/resource/borglicense.txt\", Resource.getResourceString(\"License\")));\n\t\tmv.addModule(new FileView(System.getProperty(\"user.home\", \"\") + \"/.borg.log\",\n\t\t\t\tResource.getResourceString(\"view_log\")));\n\t\tmv.addModule(new SyncModule());\n\t\tmv.addModule(new UploadModule());\n\n\t\t// allow start to system tray if option set and there is a system tray\n\t\tboolean bgStart = Prefs.getBoolPref(PrefName.BACKGSTART) && TrayIconProxy.hasTrayIcon();\n\n\t\t// make the main window visible\n\t\tif (!bgStart) {\n\t\t\tmv.setVisible(true);\n\t\t}\n\n\t\t// open all views that should be shown at startup\n\t\tmv.startupViews(bgStart);\n\n\t\t// destroy the splash screen\n\t\tif (splashScreen != null) {\n\t\t\tsplashScreen.dispose();\n\t\t\tsplashScreen = null;\n\t\t}\n\t}\n\n\t/**\n\t * raise the UI to the front\n\t */\n\tpublic static void toFront() {\n\t\tMultiView.getMainView().setVisible(true);\n\t\tMultiView.getMainView().toFront();\n\t\tMultiView.getMainView().setState(Frame.NORMAL);\n\t}\n\n\t/**\n\t * shuts down the UI, including db backup\n\t */\n\tpublic static void shutDownUI() {\n\n\t\t// warn about syncing\n\t\ttry {\n\t\t\tif (SyncLog.getReference().isProcessUpdates() && !SyncLog.getReference().getAll().isEmpty()) {\n\t\t\t\tint res = JOptionPane.showConfirmDialog(null, Resource.getResourceString(\"Sync-Warn\"), null,\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);\n\n\t\t\t\tif (res != JOptionPane.YES_OPTION) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t}\n\n\t\t// prompt for shutdown and backup options\n\t\tboolean do_backup = false;\n\t\tfinal String backupdir = Prefs.getPref(PrefName.BACKUPDIR);\n\n\t\tif (backupdir != null && !backupdir.equals(\"\")) {\n\n\t\t\tString shutdown_action = Prefs.getPref(PrefName.SHUTDOWN_ACTION);\n\t\t\tif (shutdown_action.isEmpty() || SHUTDOWN_ACTION.PROMPT.toString().equals(shutdown_action)) {\n\t\t\t\tJCheckBox b1 = new JCheckBox(Resource.getResourceString(\"backup_notice\") + \" \" + backupdir);\n\n\t\t\t\tObject[] array = { b1 };\n\n\t\t\t\tint res = JOptionPane.showConfirmDialog(null, array, Resource.getResourceString(\"shutdown_options\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\n\t\t\t\tif (res != JOptionPane.YES_OPTION) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (b1.isSelected())\n\t\t\t\t\tdo_backup = true;\n\n\t\t\t} else if (SHUTDOWN_ACTION.BACKUP.toString().equals(shutdown_action)) {\n\t\t\t\tdo_backup = true;\n\t\t\t}\n\t\t}\n\n\t\tif (do_backup == true) {\n\t\t\tFile f = new File(backupdir);\n\n\t\t\t// verify backup dir\n\t\t\tif (!f.exists()) {\n\t\t\t\ttry {\n\t\t\t\t\tf.mkdir();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// handle the error below\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!f.isDirectory() || !f.canWrite()) {\n\t\t\t\tString msg = MessageFormat.format(Resource.getResourceString(\"backup_dir_error\"), backupdir);\n\t\t\t\tErrmsg.getErrorHandler().notice(msg);\n\t\t\t}\n\n\t\t}\n\n\t\t// stop popup timer and destroy popups\n\t\tReminderManager rm = ReminderManager.getReminderManager();\n\t\tif (rm != null)\n\t\t\trm.remove();\n\n\t\tnew NonUIShutdown(do_backup).execute();\n\n\t\t// show a splash screen for shutdown which locks the UI\n\t\ttry {\n\t\t\tSplashScreen ban = new SplashScreen(ModalityType.APPLICATION_MODAL);\n\t\t\tban.setText(Resource.getResourceString(\"shutdown\"));\n\t\t\tban.setVisible(true);\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tstatic private class NonUIShutdown extends SwingWorker<Object, Object> {\n\n\t\tprivate final boolean do_backup;\n\n\t\tpublic NonUIShutdown(boolean b) {\n\t\t\tdo_backup = b;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object doInBackground() throws Exception {\n\t\t\t// backup data\n\t\t\tif (do_backup == true) {\n\t\t\t\ttry {\n\t\t\t\t\tfinal String backupdir = Prefs.getPref(PrefName.BACKUPDIR);\n\t\t\t\t\tExportImport.exportToZip(backupdir);\n\t\t\t\t\tlog.info(\"Export to ZIP Complete\");\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// non-UI shutdown\n\t\t\tBorg.shutdown();\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/UploadModule.java",
    "content": "// New class: BORGCalendar/swingui/src/main/java/net/sf/borg/ui/UploadModule.java - by copilot\n\npackage net.sf.borg.ui;\n\nimport java.awt.Component;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.JButton;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.db.jdbc.DbDirtyManager;\nimport net.sf.borg.model.sync.google.GDrive;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.util.IconHelper;\n\npublic class UploadModule implements Module, Prefs.Listener, Model.Listener {\n\n\t//static private final java.util.logging.Logger log = \n\t//\tjava.util.logging.Logger.getLogger(\"net.sf.borg\");\n\t\n\n\tprivate JButton uploadToolbarButton = null;\n\t\n\tpublic UploadModule() {\n\t}\n\n\t@Override\n\tpublic Component getComponent() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn \"UPLOAD\";\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tPrefs.addListener(this);\n\t\tDbDirtyManager.getReference().addListener(new DbDirtyManager.DbDirtyListener() {\n\t\t\t@Override\n\t\t\tpublic void onDbDirty() {\n\t\t\t\tonDatabaseUpdate();\n\t\t\t}\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void onDbClean() {\n\t\t\t\tonDatabaseUploaded();\n\t\t\t}\n\t\t});\n\t\t// Add toolbar button for upload status\n\t\tuploadToolbarButton = MultiView.getMainView().addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/Up16.gif\"), \n\t\t\t\t\"Upload\", \n\t\t\t\tnew ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tGDrive.getReference().vacuumAndUpload();\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tString usetray = Prefs.getPref(PrefName.USESYSTRAY);\n\t\tif (usetray.equals(\"true\")) {\n\t\t\ttry {\n\t\t\t\tupdateUploadButton();\n\t\t\t\tshowUploadTrayIcon();\n\t\t\t} catch (Exception e1) {\n\t\t\t\te1.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\t\ttry {\n\t\t\tupdateUploadButton();\n\t\t\tshowUploadTrayIcon();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t/**\n\t * Called when database is updated via SQLite hook.\n\t * Set the flag indicating uploads are pending.\n\t */\n\tpublic void onDatabaseUpdate() {\n\t\ttry {\n\t\t\tupdateUploadButton();\n\t\t\tshowUploadTrayIcon();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t/**\n\t * Called when database is successfully uploaded to Google Drive.\n\t * Clear the pending flag and hide the upload indicators.\n\t */\n\tpublic void onDatabaseUploaded() {\n\t\ttry {\n\t\t\tupdateUploadButton();\n\t\t\tshowUploadTrayIcon();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tprivate void updateUploadButton() throws Exception {\n\t\tif (DbDirtyManager.getReference().isDirty() && GDrive.getReference().isUploading()) {\n\t\t\t//uploadToolbarButton.setText(\"Upload\");\n\t\t\tuploadToolbarButton.setVisible(true);\n\t\t} else {\n\t\t\tuploadToolbarButton.setVisible(false);\n\t\t}\n\t}\n\n\tprivate void showUploadTrayIcon() {\n\t\tif (TrayIconProxy.hasTrayIcon()) {\n\t\t\ttry {\n\t\t\t\tif (DbDirtyManager.getReference().isDirty() && GDrive.getReference().isUploading()) {\n\t\t\t\t\t// Show the upload tray icon\n\t\t\t\t\tTrayIconProxy.enableUploadTrayIcon();\n\t\t\t\t} else {\n\t\t\t\t\t// Hide the upload tray icon\n\t\t\t\t\tTrayIconProxy.disableUploadTrayIcon();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// Any model change means database was updated\n\t\tonDatabaseUpdate();\n\t}\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/View.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\n/*\n * View.java\n *\n * Created on May 23, 2003, 11:06 AM\n */\n\npackage net.sf.borg.ui;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.model.Model;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\n\n\n/**\n * base class for borg views. contains common logic for borg windows. Views are JFrames that optionally listen for\n * change messages from models\n *\n */\npublic abstract class View extends JFrame implements Model.Listener {\n\t\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The icon image. */\n\tstatic Image image = Toolkit.getDefaultToolkit().getImage(\n\t\t\tView.class.getResource(\"/resource/borg32x32.jpg\"));\n\n\t/**\n\t * Instantiates a new view.\n\t */\n\tpublic View() {\n\t\tinitialize();\n\t}\n\n\t\n\n\t/**\n\t * Destroy the view\n\t */\n\tpublic abstract void destroy();\n\n\t/**\n\t * Initialize the view\n\t */\n\tprivate void initialize() {\n\t\tsetIconImage(image);\n\t}\n\n\t/**\n\t * registers this view for size management. once called, this view will \n\t * store its size and position via the given preference whenever the user changes the\n\t * size or position\n\t * \n\t * @param pname the pname\n\t */\n\tpublic void manageMySize(PrefName pname) {\n\t\tViewSize.manageMySize(this, pname);\n\t}\n\n\t\n\t/**\n\t * refresh the view. called when one of the registered models changes. can\n\t * be called any time a refresh is needed as well.\n\t */\n\tpublic abstract void refresh();\n\n\t/**\n\t * register the escape key to activate a button (presumably a dismiss button)\n\t * \n\t * @param bn the button\n\t */\n\tprotected void setDismissButton(final JButton bn) {\n\t\tgetLayeredPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\tbn.getActionListeners()[0].actionPerformed(e);\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/ViewSize.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui;\n\nimport lombok.Data;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * ViewSize contains the data associated with a windows's position, size, and\n * maximization state It also contains the logic to convert this data to and\n * from a String so that it can be stored.\n */\n@Data\nclass ViewSize {\n\n\t/**\n\t * DockType stores the various dock statuses for a view - always DOCK, always\n\t * UNDOCK, or NOT_SET - whoch means to use the default preference\n\t */\n\tpublic enum DockType {\n\t\tDOCK, UNDOCK\n\t}\n\n\t/**\n\t * store the views size and position.\n\t * \n\t * @param resize true if the window is being resized\n\t */\n\tstatic private void recordSize(JFrame c, PrefName pn, boolean resize) {\n\t\tString s = Prefs.getPref(pn);\n\t\tViewSize vsnew = ViewSize.fromString(s);\n\n\t\tif (!resize) {\n\t\t\t// for a move, ignore the event if the size changes - this is\n\t\t\t// part of a maximize or minimize and the resize will follow\n\t\t\tif (vsnew.getHeight() != c.getBounds().height || vsnew.getWidth() != c.getBounds().width)\n\t\t\t\treturn;\n\n\t\t\t// if x or y < 0, then this is likely the move before a maximize\n\t\t\t// so ignore it\n\t\t\tif (c.getBounds().x < 0 || c.getBounds().y < 0)\n\t\t\t\treturn;\n\n\t\t\tvsnew.setX(c.getBounds().x);\n\t\t\tvsnew.setY(c.getBounds().y);\n\t\t\tvsnew.setWidth(c.getBounds().width);\n\t\t\tvsnew.setHeight(c.getBounds().height);\n\t\t} else if (c.getExtendedState() == Frame.MAXIMIZED_BOTH) {\n\t\t\tvsnew.setMaximized(true);\n\t\t} else {\n\t\t\t// only reset bounds if we are not maximized\n\t\t\tvsnew.setMaximized(false);\n\t\t\tvsnew.setX(c.getBounds().x);\n\t\t\tvsnew.setY(c.getBounds().y);\n\t\t\tvsnew.setWidth(c.getBounds().width);\n\t\t\tvsnew.setHeight(c.getBounds().height);\n\n\t\t}\n\n\t\tPrefs.putPref(pn, vsnew.toString());\n\n\t}\n\n\t/**\n\t * Sets the window size and position from the stored preference and then sets up\n\t * listeners to store any updates to the window size and position based on user\n\t * actions.\n\t * \n\t * @param pname the preference name\n\t * @return the ViewSize object, which may be of use to the caller\n\t */\n\tstatic void manageMySize(JFrame frame, PrefName pname) {\n\n\t\t// set the initial size\n\t\tString s = Prefs.getPref(pname);\n\t\tViewSize vs = ViewSize.fromString(s);\n\t\tvs.setDock(DockType.UNDOCK);\n\t\tPrefs.putPref(pname, vs.toString());\n\n\t\t// get dimensions from pref or use defaults\n\t\tint x = (vs.getX() != -1) ? vs.getX() : 0;\n\t\tint y = (vs.getY() != -1) ? vs.getY() : 0;\n\t\tint w = (vs.getWidth() != -1) ? vs.getWidth() : 800;\n\t\tint h = (vs.getHeight() != -1) ? vs.getHeight() : 600;\n\n\t\t// determine if the window is offscreen\n\t\tboolean isOffscreen = true;\n\t\tGraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();\n\t\tfor (GraphicsDevice device : devices) {\n\t\t\tif (device.getDefaultConfiguration().getBounds().contains(x, y)) {\n\t\t\t\tisOffscreen = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif( isOffscreen )\n\t\t{\n\t\t\tx = 0; y = 0;\n\t\t}\n\n\t\tframe.setBounds(new Rectangle(x, y, w, h));\n\n\t\tif (vs.isMaximized()) {\n\t\t\tframe.setExtendedState(Frame.MAXIMIZED_BOTH);\n\t\t}\n\n\t\tframe.validate();\n\n\t\tfinal PrefName pn = pname;\n\n\t\t// add listeners to record any changes\n\t\tframe.addComponentListener(new java.awt.event.ComponentAdapter() {\n\t\t\t@Override\n\t\t\tpublic void componentMoved(java.awt.event.ComponentEvent e) {\n\t\t\t\trecordSize((JFrame) e.getComponent(), pn, false);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void componentResized(java.awt.event.ComponentEvent e) {\n\t\t\t\trecordSize((JFrame) e.getComponent(), pn, true);\n\t\t\t}\n\t\t});\n\n\t}\n\n\t/**\n\t * creates a ViewSize instance From a string.\n\t * \n\t * @param s the string\n\t * \n\t * @return the ViewSize instance\n\t */\n\tstatic public ViewSize fromString(String s) {\n\t\tViewSize vs = new ViewSize();\n\t\tString[] toks = s.split(\",\");\n\t\tvs.x = Integer.parseInt(toks[0]);\n\t\tvs.y = Integer.parseInt(toks[1]);\n\t\tvs.width = Integer.parseInt(toks[2]);\n\t\tvs.height = Integer.parseInt(toks[3]);\n\t\tvs.maximized = toks[4].equals(\"Y\");\n\t\tif (toks.length > 5) {\n\t\t\ttry {\n\t\t\t\tvs.dock = DockType.valueOf(toks[5]);\n\t\t\t} catch (Exception e) {\n\t\t\t\tvs.dock = DockType.DOCK;\n\t\t\t}\n\t\t}\n\n\t\treturn (vs);\n\t}\n\n\t// size and position\n\tprivate int height = -1;\n\tprivate int width = -1;\n\tprivate int x = -1;\n\tprivate int y = -1;\n\tprivate boolean maximized = false;\n\n\t// state\n\tprivate DockType dock = DockType.DOCK;\n\n\t/**\n\t * get the ViewSize data in a string\n\t * \n\t * @return the string\n\t */\n\t@Override\n\tpublic String toString() {\n\n\t\treturn (x + \",\" + y + \",\" + width + \",\"\n\t\t\t\t+ height + \",\" + ((maximized == true) ? \"Y\" : \"N\") + \",\" + dock.toString());\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/address/AddrListView.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.address;\n\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\n\nimport javax.swing.DefaultListSelectionModel;\nimport javax.swing.JButton;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.event.TableModelEvent;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * UI that displays the address book in tabular form and allows actions to be\n * taken on addresses\n * \n */\npublic class AddrListView extends DockableView implements Module {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Collection<Address> addrs_; // list of rows currently displayed\n\n\t// action listeners for buttons that are reused in the menu items\n\tprivate ActionListener alAddNew, alEdit, alDelete, alFind;\n\n\t// various buttons\n\tprivate JButton delbutton;\n\tprivate JButton editbutton;\n\tprivate JButton findbutton;\n\tprivate JButton newbutton;\n\n\t// scroll for table\n\tprivate JScrollPane tableScrollPane;\n\n\t// the table\n\tprivate JTable addressTable;\n\t\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t */\n\tpublic AddrListView() {\n\t\tsuper();\n\t}\n\n\t/**\n\t * delete action\n\t */\n\tprivate void delbuttonActionPerformed() {\n\t\t// figure out which row is selected to be deleted\n\t\tint[] indices = addressTable.getSelectedRows();\n\t\tif (indices.length == 0)\n\t\t\treturn;\n\n\t\t// confirm delete\n\t\tint ret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t.getResourceString(\"Delete_Addresses\"), Resource\n\t\t\t\t.getResourceString(\"Delete\"), JOptionPane.OK_CANCEL_OPTION,\n\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\treturn;\n\n\t\t// collect the addresses. cannot delete them in this loop\n\t\t// as that would change the indexes\n\t\tCollection<Address> addresses = new ArrayList<Address>();\n\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\tint index = indices[i];\n\t\t\t// need to ask the table for the original (befor sorting) index\n\t\t\t// of the selected row\n\t\t\tTableSorter tm = (TableSorter) addressTable.getModel();\n\t\t\tint k = tm.getMappedIndex(index); // get original index - not\n\t\t\t// current sorted position\n\t\t\t// in tbl\n\t\t\tObject[] oa = addrs_.toArray();\n\t\t\tAddress addr = (Address) oa[k];\n\t\t\taddresses.add(addr);\n\n\t\t}\n\n\t\t// delete the addresses\n\t\tfor (Address addr : addresses) {\n\t\t\tAddressModel.getReference().delete(addr);\n\t\t}\n\n\t}\n\n\t/**\n\t * edit an address\n\t */\n\tprivate void editRow() {\n\t\t// figure out which row is selected.\n\t\tint index = addressTable.getSelectedRow();\n\t\tif (index == -1)\n\t\t\treturn;\n\t\taddressTable.getSelectionModel().setSelectionInterval(index, index);\n\t\t// ensure only one row is selected.\n\n\t\ttry {\n\t\t\t// need to ask the table for the original (befor sorting) index of\n\t\t\t// the selected row\n\t\t\tTableSorter tm = (TableSorter) addressTable.getModel();\n\t\t\tint k = tm.getMappedIndex(index); // get original index - not\n\t\t\t// current sorted position in\n\t\t\t// tbl\n\t\t\tObject[] oa = addrs_.toArray();\n\t\t\tAddress addr = (Address) oa[k];\n\n\t\t\t// show an address editor for the address\n\t\t\tnew AddressView(addr).showView();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * find action\n\t */\n\tprivate void findbuttonActionPerformed() {\n\n\t\t// Search for a string\n\t\tString searchstring = \"\";\n\t\tJScrollBar vScrollBar = null;\n\t\tdo {\n\t\t\tsearchstring = JOptionPane.showInputDialog(null, Resource\n\t\t\t\t\t.getResourceString(\"Search_For\"), searchstring);\n\t\t\tif (searchstring != null) {\n\t\t\t\tint tablerowcount = addressTable.getRowCount();\n\t\t\t\tint tablecolcount = addressTable.getColumnCount();\n\n\t\t\t\tfor (int iRow = 0; iRow < tablerowcount; iRow++) {\n\t\t\t\t\tfor (int jCol = 0; jCol < tablecolcount; jCol++) {\n\t\t\t\t\t\tString colvalue = null;\n\t\t\t\t\t\tjava.util.Date colvaluedt = null;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcolvalue = (String) addressTable.getValueAt(iRow,\n\t\t\t\t\t\t\t\t\tjCol);\n\t\t\t\t\t\t} catch (ClassCastException ccex) {\n\t\t\t\t\t\t\tcolvaluedt = (java.util.Date) addressTable\n\t\t\t\t\t\t\t\t\t.getValueAt(iRow, jCol);\n\t\t\t\t\t\t\tcolvalue = colvaluedt.toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (searchstring != null) {\n\t\t\t\t\t\t\tboolean match = searchstring\n\t\t\t\t\t\t\t\t\t.equalsIgnoreCase(colvalue);\n\t\t\t\t\t\t\tif (!match && colvalue != null) {\n\t\t\t\t\t\t\t\tString colvalue2 = colvalue.toLowerCase();\n\t\t\t\t\t\t\t\tString searchstring2 = searchstring\n\t\t\t\t\t\t\t\t\t\t.toLowerCase();\n\t\t\t\t\t\t\t\tint indexint = colvalue2.indexOf(searchstring2\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tmatch = indexint > -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\taddressTable\n\t\t\t\t\t\t\t\t\t\t.setRowSelectionInterval(iRow, iRow);\n\t\t\t\t\t\t\t\tvScrollBar = tableScrollPane\n\t\t\t\t\t\t\t\t\t\t.getVerticalScrollBar();\n\n\t\t\t\t\t\t\t\tint maxVal = vScrollBar.getMaximum();\n\t\t\t\t\t\t\t\tint oneCellScrollValue = maxVal / tablerowcount;\n\n\t\t\t\t\t\t\t\tvScrollBar.setValue(iRow * oneCellScrollValue);\n\t\t\t\t\t\t\t\tsearchstring = JOptionPane\n\t\t\t\t\t\t\t\t\t\t.showInputDialog(\n\t\t\t\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t\t\t\tResource\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getResourceString(\"Search_Next\"),\n\t\t\t\t\t\t\t\t\t\t\t\tsearchstring);\n\t\t\t\t\t\t\t\tiRow++;\n\t\t\t\t\t\t\t\tjCol=0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (searchstring != null) {\n\t\t\t\t\tJOptionPane.showMessageDialog(null, Resource\n\t\t\t\t\t\t\t.getResourceString(\"Not_Found_End\"));\n\t\t\t\t}\n\t\t\t}\n\t\t} while (searchstring != null);\n\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn Resource.getResourceString(\"Address_Book\");\n\t}\n\n\n\t/**\n\t * init the UI components\n\t */\n\tprivate void initComponents() {\n\n\t\ttableScrollPane = new JScrollPane();\n\t\taddressTable = new JTable();\n\t\tJPanel buttonPanel = new JPanel();\n\t\tnewbutton = new JButton();\n\t\teditbutton = new JButton();\n\t\tdelbutton = new JButton();\n\t\tfindbutton = new JButton();\n\n\t\ttableScrollPane.setPreferredSize(new java.awt.Dimension(554, 404));\n\n\t\tDefaultListSelectionModel mylsmodel = new DefaultListSelectionModel();\n\t\tmylsmodel\n\t\t\t\t.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\t\taddressTable.setSelectionModel(mylsmodel);\n\t\taddressTable.addMouseListener(new java.awt.event.MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(java.awt.event.MouseEvent evt) {\n\t\t\t\tif (evt.getClickCount() < 2)\n\t\t\t\t\treturn;\n\t\t\t\teditRow();\n\t\t\t}\n\t\t});\n\n\t\ttableScrollPane.setViewportView(addressTable);\n\t\taddressTable.setShowGrid(true);\n\t\taddressTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tadd(tableScrollPane, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\tnewbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Add16.gif\"));\n\t\tResourceHelper.setText(newbutton, \"Add_New\");\n\t\tnewbutton\n\t\t\t\t.addActionListener(alAddNew = new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tAddress addr = AddressModel.getReference().newAddress();\n\t\t\t\t\t\taddr.setKey(-1);\n\t\t\t\t\t\tnew AddressView(addr).showView();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tbuttonPanel.add(newbutton);\n\n\t\teditbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Edit16.gif\"));\n\t\tResourceHelper.setText(editbutton, \"Edit\");\n\t\teditbutton\n\t\t\t\t.addActionListener(alEdit = new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\teditRow();\n\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tbuttonPanel.add(editbutton);\n\n\t\tdelbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Delete16.gif\"));\n\t\tResourceHelper.setText(delbutton, \"Delete\");\n\t\tdelbutton\n\t\t\t\t.addActionListener(alDelete = new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tdelbuttonActionPerformed();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tbuttonPanel.add(delbutton);\n\n\t\t// Find Button - Search a text in Address List\n\t\tfindbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Find16.gif\"));\n\t\tResourceHelper.setText(findbutton, \"Find\");\n\t\tfindbutton\n\t\t\t\t.addActionListener(alFind = new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tfindbuttonActionPerformed();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tbuttonPanel.add(findbutton);\n\n\t\tadd(buttonPanel, GridBagConstraintsFactory.create(0, 1));\n\n\t\t// add context menu actions\n\t\tnew PopupMenuHelper(addressTable, new PopupMenuHelper.Entry[] {\n\t\t\t\tnew PopupMenuHelper.Entry(alAddNew, \"Add_New\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alEdit, \"Edit\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alDelete, \"Delete\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alFind, \"Find\") });\n\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\tAddressModel addrmod_ = AddressModel.getReference();\n\n\t\ttry {\n\t\t\taddrs_ = addrmod_.getAddresses();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t\t// init the table to empty\n\t\tTableSorter tm = (TableSorter) addressTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(addressTable);\n\t\ttm.setRowCount(0);\n\n\t\tIterator<Address> it = addrs_.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tAddress r = it.next();\n\n\t\t\ttry {\n\n\t\t\t\t// add the table row\n\t\t\t\tObject[] ro = new Object[7];\n\t\t\t\tro[0] = r.getFirstName();\n\t\t\t\tro[1] = r.getLastName();\n\t\t\t\tro[2] = r.getEmail();\n\t\t\t\tro[3] = r.getCellPhone();\n\t\t\t\tro[4] = r.getHomePhone();\n\t\t\t\tro[5] = r.getWorkPhone();\n\t\t\t\tro[6] = r.getBirthday();\n\t\t\t\ttm.addRow(ro);\n\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\n\t\t// sort the table by last name\n\t\ttm.sortByColumn(1);\n\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Address_Book\");\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\t\t\n\t\taddModel(AddressModel.getReference());\n\n\t\tif( !isInitialized)\n\t\t{\n\n\t\t\tthis.setLayout(new GridBagLayout());\n\n\t\t\t// init the gui components\n\t\t\tinitComponents();\n\n\t\t\t// set the column headings and types\n\t\t\taddressTable.setModel(new TableSorter(new String[] {\n\t\t\t\t\tResource.getResourceString(\"First\"),\n\t\t\t\t\tResource.getResourceString(\"Last\"),\n\t\t\t\t\tResource.getResourceString(\"Email\"),\n\t\t\t\t\tResource.getResourceString(\"Cell_Phone:\"),\n\t\t\t\t\tResource.getResourceString(\"Home_Phone\"),\n\t\t\t\t\tResource.getResourceString(\"Work_Phone\"),\n\t\t\t\t\tResource.getResourceString(\"Birthday\") }, new Class[] {\n\t\t\t\t\tjava.lang.String.class, java.lang.String.class,\n\t\t\t\t\tjava.lang.String.class, java.lang.String.class,\n\t\t\t\t\tjava.lang.String.class, java.lang.String.class,\n\t\t\t\t\tjava.util.Date.class }));\n\n\t\t\trefresh();\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(IconHelper.getIcon(\n\t\t\t\t\"/resource/addr.png\"), getModuleName(),\n\t\t\t\tnew ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(getViewType());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(),\n\t\t\t\tnew ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(getViewType());\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tTablePrinter.printTable(addressTable);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.ADDRESS;\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/address/AddressView.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.address;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.util.Date;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextArea;\nimport javax.swing.JTextField;\nimport javax.swing.SwingConstants;\nimport javax.swing.border.TitledBorder;\n\nimport com.toedter.calendar.JDateChooser;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.link.LinkPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.LimitDocument;\nimport net.sf.borg.ui.util.PlainDateEditor;\n\n/**\n * UI for editing a single address record.\n * \n */\npublic class AddressView extends DockableView {\n\n\t@Override\n\tprotected void cleanUp() {\n\t\tsuper.cleanUp();\n\t\tif( linkPanel != null )\n\t\t\tlinkPanel.cleanup();\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\t\n\t// address being edited\n\tprivate final Address addr_;\n\n\t// the address fields\n\tprivate JDateChooser birthdayChooser;\n\tprivate JTextField cityText;\n\tprivate JTextField companyText;\n\tprivate JTextField countryText;\n\tprivate JTextField emailText;\n\tprivate JTextField cellPhoneText;\n\tprivate JTextField faxText;\n\tprivate JTextField firstNameText;\n\tprivate JTextField homePageText;\n\tprivate JTextField lastNameText;\n\tprivate JTextField nickNameText;\n\tprivate JTextArea notesText;\n\tprivate JTextField pagerText;\n\tprivate JTextField screenNameText;\n\tprivate JTextField stateText;\n\tprivate JTextField streetAddresText;\n\tprivate JTextField webPageText;\n\tprivate JTextField workCityText;\n\tprivate JTextField workCountryText;\n\tprivate JTextField workPhoneText;\n\tprivate JTextField workStateText;\n\tprivate JTextField workStreetAddressText;\n\tprivate JTextField workZipText;\n\tprivate JTextField zipCodeText;\n\n\t// link panel\n\tprivate LinkPanel linkPanel;\n\n\t/**\n\t * constructor\n\t * \n\t * @param addr\n\t *            address to load into the UI\n\t */\n\tpublic AddressView(Address addr) {\n\t\tsuper();\n\n\t\taddr_ = addr;\n\n\t\taddModel(AddressModel.getReference());\n\n\t\tinitComponents(); // init the GUI widgets\n\n\t\t// display the window\n\t\tshowaddr();\n\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn Resource.getResourceString(\"Address_Book_Entry\");\n\t}\n\n\t/**\n\t * initialize the UI\n\t */\n\t// this method is generated code that was cleaned up. It is much better, but\n\t// still, some stupid label names remain\n\tprivate void initComponents()\n\t{\n\n\t\tJPanel contactPanel = new JPanel();\n\t\tfirstNameText = new JTextField(new LimitDocument(200), null, 25);\n\t\tlastNameText = new JTextField(new LimitDocument(200), null, 25);\n\t\tnickNameText = new JTextField(new LimitDocument(200), null, 25);\n\t\tscreenNameText = new JTextField(new LimitDocument(200), null, 25);\n\t\thomePageText = new JTextField(new LimitDocument(200), null, 25);\n\t\tworkPhoneText = new JTextField(new LimitDocument(200), null, 25);\n\t\tpagerText = new JTextField(new LimitDocument(200), null, 25);\n\t\tfaxText = new JTextField(new LimitDocument(200), null, 25);\n\t\temailText = new JTextField(new LimitDocument(200), null, 50);\n\t\twebPageText = new JTextField(new LimitDocument(200), null, 100);\n\t\tcompanyText = new JTextField(new LimitDocument(200), null, 25);\n\t\tbirthdayChooser = new JDateChooser(new PlainDateEditor());\n\t\tcellPhoneText = new JTextField(new LimitDocument(200),null,25);\n\t\tJPanel homeAddressPanel = new JPanel();\n\t\tstreetAddresText = new JTextField(new LimitDocument(200), null, 25);\n\t\tcityText = new JTextField(new LimitDocument(200), null, 25);\n\t\tstateText = new JTextField(new LimitDocument(200), null, 25);\n\t\tcountryText = new JTextField(new LimitDocument(200), null, 25);\n\t\tzipCodeText = new JTextField(new LimitDocument(200), null, 25);\n\t\tJPanel workAddressPanel = new JPanel();\n\t\tworkZipText = new JTextField(new LimitDocument(200), null, 25);\n\t\tworkCountryText = new JTextField(new LimitDocument(200), null, 25);\n\t\tworkStateText = new JTextField(new LimitDocument(200), null, 25);\n\t\tworkCityText = new JTextField(new LimitDocument(200), null, 25);\n\t\tworkStreetAddressText = new JTextField(new LimitDocument(200), null, 25);\n\t\tnotesText = new JTextArea(new LimitDocument(Prefs.getIntPref(PrefName.MAX_TEXT_SIZE)));\n\t\tJPanel buttonPanel = new JPanel();\n\t\tJButton saveButton = new JButton();\n\t\t\n\t\tsetLayout(new GridBagLayout());\n\n\n\t\t//\n\t\t// CONTACT\n\t\t//\n\t\tcontactPanel.setLayout(new java.awt.GridBagLayout());\n\n\t\tJLabel jLabel1 = new JLabel();\n\t\tResourceHelper.setText(jLabel1, \"First_Name:\");\n\t\tjLabel1.setLabelFor(firstNameText);\n\t\tcontactPanel.add(jLabel1, GridBagConstraintsFactory.create(0, 0));\n\n\t\tJLabel jLabel2 = new JLabel();\n\t\tResourceHelper.setText(jLabel2, \"Last_Name:\");\n\t\tjLabel2.setLabelFor(lastNameText);\n\t\tcontactPanel.add(jLabel2, GridBagConstraintsFactory.create(0, 1));\n\n\t\tJLabel jLabel3 = new JLabel();\n\t\tResourceHelper.setText(jLabel3, \"Nickname:\");\n\t\tjLabel3.setLabelFor(nickNameText);\n\t\tcontactPanel.add(jLabel3, GridBagConstraintsFactory.create(0, 2));\n\n\t\tJLabel jLabel4 = new JLabel();\n\t\tResourceHelper.setText(jLabel4, \"Screen_Name:\");\n\t\tjLabel4.setLabelFor(screenNameText);\n\t\tcontactPanel.add(jLabel4, GridBagConstraintsFactory.create(2, 0));\n\n\t\tJLabel jLabel5 = new JLabel();\n\t\tResourceHelper.setText(jLabel5, \"Home_Phone:\");\n\t\tjLabel5.setLabelFor(homePageText);\n\t\tcontactPanel.add(jLabel5, GridBagConstraintsFactory.create(2, 1));\n\n\t\tJLabel jLabel6 = new JLabel();\n\t\tResourceHelper.setText(jLabel6, \"Work_Phone:\");\n\t\tjLabel6.setLabelFor(workPhoneText);\n\t\tcontactPanel.add(jLabel6, GridBagConstraintsFactory.create(2, 2));\n\n\t\tJLabel jLabel7 = new JLabel();\n\t\tResourceHelper.setText(jLabel7, \"Pager:\");\n\t\tjLabel7.setLabelFor(pagerText);\n\t\tcontactPanel.add(jLabel7, GridBagConstraintsFactory.create(4, 0));\n\n\t\tJLabel jLabel8 = new JLabel();\n\t\tResourceHelper.setText(jLabel8, \"Fax:\");\n\t\tjLabel8.setLabelFor(faxText);\n\t\tcontactPanel.add(jLabel8, GridBagConstraintsFactory.create(4, 1));\n\n\t\tJLabel jLabel9 = new JLabel();\n\t\tResourceHelper.setText(jLabel9, \"Email:\");\n\t\tjLabel9.setLabelFor(emailText);\n\t\tcontactPanel.add(jLabel9, GridBagConstraintsFactory.create(4, 2));\n\n\t\tJLabel jLabel14 = new JLabel();\n\t\tResourceHelper.setText(jLabel14, \"Web_Page:\");\n\t\tjLabel14.setLabelFor(webPageText);\n\t\tcontactPanel.add(jLabel14, GridBagConstraintsFactory.create(6, 0));\n\n\t\tJLabel jLabel21 = new JLabel();\n\t\tResourceHelper.setText(jLabel21, \"Company\");\n\t\tjLabel21.setLabelFor(companyText);\n\t\tcontactPanel.add(jLabel21, GridBagConstraintsFactory.create(6, 1));\n\n\t\tJLabel jLabel22 = new JLabel();\n\t\tResourceHelper.setText(jLabel22, \"Birthday\");\n\t\tjLabel22.setLabelFor(birthdayChooser);\n\t\tcontactPanel.add(jLabel22, GridBagConstraintsFactory.create(6, 2));\n\t\t\n\t\tJLabel jLabel23 = new JLabel();\n\t\tResourceHelper.setText(jLabel23, \"Cell_Phone:\");\n\t\tjLabel23.setLabelFor(cellPhoneText);\n\t\tcontactPanel.add(jLabel23,GridBagConstraintsFactory.create(0,3));\n\n\t\tcontactPanel.add(firstNameText, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(lastNameText, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(nickNameText, GridBagConstraintsFactory.create(1, 2,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(screenNameText, GridBagConstraintsFactory.create(3, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(homePageText, GridBagConstraintsFactory.create(3, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(workPhoneText, GridBagConstraintsFactory.create(3, 2,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(pagerText, GridBagConstraintsFactory.create(5, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(faxText, GridBagConstraintsFactory.create(5, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(emailText, GridBagConstraintsFactory.create(5, 2,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(webPageText, GridBagConstraintsFactory.create(7, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(companyText, GridBagConstraintsFactory.create(7, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(birthdayChooser, GridBagConstraintsFactory.create(7,\n\t\t\t\t2, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tcontactPanel.add(cellPhoneText, GridBagConstraintsFactory.create(1,\n\t\t\t\t3, GridBagConstraints.BOTH,1.0,0.0));\n\n\t\tcontactPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"contact\")));\n\t\t\n\t\tGridBagConstraints cgbc = GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tcgbc.gridwidth = 2;\n\t\tadd(contactPanel, cgbc);\n\t\t\n\t\t//\n\t\t// ADDRESS\n\t\t//\t\n\t\t\n\t\t\n\t\thomeAddressPanel.setLayout(new java.awt.GridBagLayout());\n\n\t\thomeAddressPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"HomeAddress\")));\n\n\t\tJLabel jLabel12 = new JLabel();\n\t\tResourceHelper.setText(jLabel12, \"Home_State:\");\n\t\tjLabel12.setLabelFor(workStateText);\n\n\t\tJLabel jLabel13 = new JLabel();\n\t\tResourceHelper.setText(jLabel13, \"Home_Country:\");\n\t\tjLabel13.setLabelFor(workCountryText);\n\n\t\tJLabel jLabel15 = new JLabel();\n\t\tResourceHelper.setText(jLabel15, \"Home_Zip_Code:\");\n\t\tjLabel15.setLabelFor(workZipText);\n\t\t\n\t\tJLabel jLabel16 = new JLabel();\n\t\tResourceHelper.setText(jLabel16, \"Work_Street_Address\");\n\t\tjLabel16.setLabelFor(workStreetAddressText);\n\n\t\tjLabel16.setHorizontalAlignment(SwingConstants.RIGHT);\n\n\t\tJLabel jLabel17 = new JLabel();\n\t\tResourceHelper.setText(jLabel17, \"Work_City:\");\n\t\tjLabel17.setLabelFor(workCityText);\n\n\t\tjLabel17.setHorizontalAlignment(SwingConstants.RIGHT);\n\t\tjLabel17.setHorizontalTextPosition(SwingConstants.RIGHT);\n\n\n\t\thomeAddressPanel.add(jLabel16, GridBagConstraintsFactory.create(0, 0));\n\t\thomeAddressPanel.add(jLabel17, GridBagConstraintsFactory.create(0, 1));\n\t\thomeAddressPanel.add(jLabel12, GridBagConstraintsFactory.create(0, 2));\n\t\thomeAddressPanel.add(jLabel13, GridBagConstraintsFactory.create(0, 3));\n\t\thomeAddressPanel.add(jLabel15, GridBagConstraintsFactory.create(0, 4));\n\n\t\thomeAddressPanel.add(streetAddresText, GridBagConstraintsFactory\n\t\t\t\t.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\thomeAddressPanel.add(cityText, GridBagConstraintsFactory.create(1,\n\t\t\t\t1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\thomeAddressPanel.add(stateText, GridBagConstraintsFactory.create(1,\n\t\t\t\t2, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\thomeAddressPanel.add(countryText, GridBagConstraintsFactory.create(\n\t\t\t\t1, 3, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\thomeAddressPanel.add(zipCodeText, GridBagConstraintsFactory.create(1,\n\t\t\t\t4, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\t\n\t\tadd(homeAddressPanel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\n\t\tworkAddressPanel.setLayout(new java.awt.GridBagLayout());\n\n\t\tworkAddressPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"WorkAddress\")));\n\n\t\tJLabel jLabel10 = new JLabel();\n\t\tResourceHelper.setText(jLabel10, \"Home_Street_Address\");\n\t\tjLabel10.setLabelFor(streetAddresText);\n\n\t\tJLabel jLabel11 = new JLabel();\n\t\tResourceHelper.setText(jLabel11, \"Home_City:\");\n\t\tjLabel11.setLabelFor(cityText);\n\t\n\t\tJLabel jLabel18 = new JLabel();\n\t\tResourceHelper.setText(jLabel18, \"Work_State:\");\n\t\tjLabel18.setLabelFor(stateText);\n\n\t\tjLabel18.setHorizontalAlignment(SwingConstants.RIGHT);\n\n\t\tJLabel jLabel19 = new JLabel();\n\t\tResourceHelper.setText(jLabel19, \"Work_Zip_Code:\");\n\t\tjLabel19.setLabelFor(zipCodeText);\n\n\t\tjLabel19.setHorizontalAlignment(SwingConstants.RIGHT);\n\n\t\tJLabel jLabel20 = new JLabel();\n\t\tResourceHelper.setText(jLabel20, \"Work_Country:\");\n\t\tjLabel20.setLabelFor(countryText);\n\n\t\tjLabel20.setHorizontalAlignment(SwingConstants.RIGHT);\n\t\tworkStreetAddressText.setMinimumSize(new java.awt.Dimension(4, 50));\n\t\tworkAddressPanel.add(jLabel10, GridBagConstraintsFactory.create(0, 0));\n\t\tworkAddressPanel.add(jLabel11, GridBagConstraintsFactory.create(0, 1));\n\t\tworkAddressPanel.add(jLabel18, GridBagConstraintsFactory.create(0, 2));\n\t\tworkAddressPanel.add(jLabel20, GridBagConstraintsFactory.create(0, 3));\n\t\tworkAddressPanel.add(jLabel19, GridBagConstraintsFactory.create(0, 4));\n\n\t\tworkAddressPanel.add(workStreetAddressText, GridBagConstraintsFactory\n\t\t\t\t.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tworkAddressPanel.add(workCityText, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tworkAddressPanel.add(workStateText, GridBagConstraintsFactory.create(1, 2,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\tworkAddressPanel.add(workCountryText, GridBagConstraintsFactory.create(1,\n\t\t\t\t3, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tworkAddressPanel.add(workZipText, GridBagConstraintsFactory.create(1,\n\t\t\t\t4, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\t\n\n\t\tadd(workAddressPanel, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\n\t\t//\n\t\t// NOTES\n\t\t//\n\t\tnotesText.setLineWrap(true);\n\t\tnotesText.setWrapStyleWord(true);\n\t\tnotesText.setColumns(40);\n\t\tnotesText.setRows(5);\n\t\tJScrollPane sp = new JScrollPane();\n\t\tsp.setViewportView(notesText);\n\t\tJPanel notesPanel = new JPanel();\n\t\tnotesPanel.setLayout(new GridBagLayout());\n\t\tnotesPanel.add(sp, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\t\tnotesPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"Notes\")));\n\t\t\n\t\tGridBagConstraints ngbc = GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tngbc.gridwidth = 2;\n\t\tadd(notesPanel, ngbc);\n\n\t\t//\n\t\t// LINKS\n\t\t//\n\t\tlinkPanel = new LinkPanel();\n\t\tlinkPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"links\")));\n\t\t\n\t\tGridBagConstraints lgbc = GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tlgbc.gridwidth = 2;\n\t\tadd(linkPanel, lgbc);\n\t\t\n\n\t\t//\n\t\t// BUTTON\n\t\t//\n\t\tsaveButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Save16.gif\"));\n\t\tResourceHelper.setText(saveButton, \"Save\");\n\t\tsaveButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tsaveaddr();\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(saveButton);\n\t\tGridBagConstraints bgbc = GridBagConstraintsFactory.create(0,4, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tbgbc.gridwidth = 2;\n\t\tadd(buttonPanel, bgbc);\n\n\t\t\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\t// empty\n\t}\n\n\t/**\n\t * save the current address data from the UI into the database\n\t */\n\tprivate void saveaddr() {\n\n\t\tDate bd = birthdayChooser.getDate();\n\t\tif( bd != null && DateUtil.isAfter(bd, new Date()))\n\t\t{\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"future_birthday\"));\n\t\t}\n\n\t\taddr_.setFirstName(firstNameText.getText());\n\t\taddr_.setLastName(lastNameText.getText());\n\t\taddr_.setNickname(nickNameText.getText());\n\t\taddr_.setEmail(emailText.getText());\n\t\taddr_.setScreenName(screenNameText.getText());\n\t\taddr_.setWorkPhone(workPhoneText.getText());\n\t\taddr_.setHomePhone(homePageText.getText());\n\t\taddr_.setFax(faxText.getText());\n\t\taddr_.setPager(pagerText.getText());\n\t\taddr_.setWebPage(webPageText.getText());\n\t\taddr_.setNotes(notesText.getText());\n\t\taddr_.setStreetAddress(streetAddresText.getText());\n\t\taddr_.setCity(cityText.getText());\n\t\taddr_.setState(stateText.getText());\n\t\taddr_.setCountry(countryText.getText());\n\t\taddr_.setZip(zipCodeText.getText());\n\t\taddr_.setWorkStreetAddress(workStreetAddressText.getText());\n\t\taddr_.setWorkCity(workCityText.getText());\n\t\taddr_.setWorkState(workStateText.getText());\n\t\taddr_.setWorkCountry(workCountryText.getText());\n\t\taddr_.setWorkZip(workZipText.getText());\n\t\taddr_.setCompany(companyText.getText());\n\t\taddr_.setBirthday(birthdayChooser.getDate());\n\t\taddr_.setCellPhone(cellPhoneText.getText());\n\n\t\ttry {\n\t\t\tAddressModel.getReference().saveAddress(addr_);\n\n\t\t\tthis.close();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * load the current addresses data into the UI\n\t */\n\tprivate void showaddr() {\n\t\tfirstNameText.setText(addr_.getFirstName());\n\t\tlastNameText.setText(addr_.getLastName());\n\t\tnickNameText.setText(addr_.getNickname());\n\t\temailText.setText(addr_.getEmail());\n\t\tscreenNameText.setText(addr_.getScreenName());\n\t\tworkPhoneText.setText(addr_.getWorkPhone());\n\t\thomePageText.setText(addr_.getHomePhone());\n\t\tfaxText.setText(addr_.getFax());\n\t\tpagerText.setText(addr_.getPager());\n\t\twebPageText.setText(addr_.getWebPage());\n\t\tnotesText.setText(addr_.getNotes());\n\t\tstreetAddresText.setText(addr_.getStreetAddress());\n\t\tcityText.setText(addr_.getCity());\n\t\tstateText.setText(addr_.getState());\n\t\tcountryText.setText(addr_.getCountry());\n\t\tzipCodeText.setText(addr_.getZip());\n\t\tworkStreetAddressText.setText(addr_.getWorkStreetAddress());\n\t\tworkCityText.setText(addr_.getWorkCity());\n\t\tworkStateText.setText(addr_.getWorkState());\n\t\tworkCountryText.setText(addr_.getWorkCountry());\n\t\tworkZipText.setText(addr_.getWorkZip());\n\t\tcompanyText.setText(addr_.getCompany());\n\t\tcellPhoneText.setText(addr_.getCellPhone());\n\n\t\tDate bd = addr_.getBirthday();\n\t\tbirthdayChooser.setDate(bd);\n\n\t\tlinkPanel.setOwner(addr_);\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// empty\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/address/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Provides UI classes for Addresses\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/AppointmentListView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionListener;\nimport java.beans.PropertyChangeEvent;\nimport java.beans.PropertyChangeListener;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.SwingUtilities;\nimport javax.swing.border.LineBorder;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.event.ListSelectionListener;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.TableCellRenderer;\nimport javax.swing.table.TableColumn;\nimport javax.swing.table.TableColumnModel;\n\nimport com.toedter.calendar.JDateChooser;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.EmailReminder;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.util.DateDialog;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.MyDateChooser;\nimport net.sf.borg.ui.util.PasswordHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * AppointmentListView provides a UI for editing the appoitnments for a day. The\n * UI presents a list of appointments and allows the user to edit the selected\n * appointment in an appointment editor.\n */\npublic class AppointmentListView extends DockableView implements\n\t\tListSelectionListener {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t@Override\n\tprotected void cleanUp() {\n\t\tsuper.cleanUp();\n\t\tif( appointmentPanel != null )\n\t\t\tappointmentPanel.cleanup();\n\t}\n\n\t/**\n\t * show the time of an appointment in the appt table - and shopw dashes for\n\t * appointments with no time\n\t */\n\tprivate class TimeRenderer extends JLabel implements TableCellRenderer {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t/**\n\t\t * constructor\n\t\t */\n\t\tpublic TimeRenderer() {\n\t\t\tsuper();\n\t\t\tsetOpaque(true); // MUST do this for background to show up.\n\t\t}\n\n\t\t/**\n\t\t * render the cell\n\t\t */\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table,\n\t\t\t\tObject obj, boolean isSelected, boolean hasFocus, int row,\n\t\t\t\tint column) {\n\n\t\t\tComponent c = defrend.getTableCellRendererComponent(table, obj,\n\t\t\t\t\tisSelected, hasFocus, row, column);\n\t\t\tthis.setBackground(c.getBackground());\n\t\t\tthis.setForeground(c.getForeground());\n\n\t\t\tDate d = (Date) obj;\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tcal.setTime(d);\n\t\t\t// seconds field is set to zero only for untimed appts\n\t\t\tif (cal.get(Calendar.SECOND) == 0) {\n\t\t\t\tthis.setText(\"--------\");\n\t\t\t} else {\n\t\t\t\tSimpleDateFormat sdf = AppointmentModel.getTimeFormat();\n\t\t\t\tthis.setText(sdf.format(d));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t/**\n\t * Prompt the user to change the date of a set of appointments\n\t * \n\t * @param appts\n\t *            the appts\n\t */\n\tstatic void onChangeDate(List<Appointment> appts) {\n\t\tif (appts.size() == 0)\n\t\t\treturn;\n\n\t\tDateDialog dlg = new DateDialog(null);\n\t\tCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(appts.get(0).getDate());\n\t\tdlg.setCalendar(cal);\n\t\tdlg.setVisible(true);\n\t\tCalendar dlgcal = dlg.getCalendar();\n\t\tif (dlgcal == null)\n\t\t\treturn;\n\n\t\tcal.set(dlgcal.get(Calendar.YEAR), dlgcal.get(Calendar.MONTH), dlgcal\n\t\t\t\t.get(Calendar.DAY_OF_MONTH));\n\n\t\tfor (int i = 0; i < appts.size(); ++i) {\n\t\t\ttry {\n\t\t\t\tAppointment appt = appts.get(i);\n\t\t\t\tappt.setDate(cal.getTime());\n\t\t\t\tAppointmentModel.getReference().saveAppt(appt);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Move a set of appointments to the following day\n\t * \n\t * @param appts\n\t *            the appts\n\t */\n\tstatic void onMoveToFollowingDay(List<Appointment> appts) {\n\t\tif (appts.size() == 0)\n\t\t\treturn;\n\n\t\tfor (int i = 0; i < appts.size(); ++i) {\n\t\t\ttry {\n\t\t\t\tAppointment appt = appts.get(i);\n\n\t\t\t\t// Increment the day\n\t\t\t\tGregorianCalendar gcal = new GregorianCalendar();\n\t\t\t\tgcal.setTime(appt.getDate());\n\t\t\t\tgcal.add(Calendar.DAY_OF_YEAR, 1);\n\t\t\t\tappt.setDate(gcal.getTime());\n\t\t\t\tappt.setSkipList(null);\n\t\t\t\tappt.setNextTodo(null);\n\t\t\t\tAppointmentModel.getReference().saveAppt(appt);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate JButton addButton;\n\tprivate AppointmentPanel appointmentPanel = null;\n\tprivate JTable apptTable;\n\tprivate GregorianCalendar cal_ = null; // date of the appt list\n\tprivate MyDateChooser dateChooser = null;\n\tprivate JButton copyButton = null;\n\tprivate TableCellRenderer defrend = null; // default table cell renderer\n\tprivate JButton deleteButton;\n\tprivate JButton deleteOneOnlyButton;\n\tprivate JPanel buttonPanel;\n\tprivate JPanel appointmentListPanel;\n\tprivate JButton reminderButton = null;\n\tprivate String title_ = \"\"; // tab/window title\n\n\t/**\n\t * constructor\n\t * \n\t * @param year\n\t *            the year\n\t * @param month\n\t *            the month\n\t * @param day\n\t *            the day\n\t */\n\tpublic AppointmentListView(int year, int month, int day) {\n\t\tsuper();\n\n\t\taddModel(AppointmentModel.getReference());\n\t\taddModel(TaskModel.getReference());\n\n\t\tappointmentPanel = new AppointmentPanel(year, month, day);\n\n\t\tinitComponents();\n\n\t\ttitle_ = Resource.getResourceString(\"Appointment_Editor\");\n\n\n\n\t\t// use a sorted table model\n\t\tapptTable.setModel(new TableSorter(new String[] {\n\t\t\t\tResource.getResourceString(\"Text\"),\n\t\t\t\tResource.getResourceString(\"Time\"), \"Key\" }, new Class[] {\n\t\t\t\tjava.lang.String.class, java.util.Date.class, Integer.class }));\n\n\t\t// set renderer to the custom one for time\n\t\tdefrend = apptTable.getDefaultRenderer(Date.class);\n\t\tapptTable.setDefaultRenderer(java.util.Date.class,\n\t\t\t\tnew AppointmentListView.TimeRenderer());\n\n\t\t// set up for sorting when a column header is clicked\n\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(apptTable);\n\n\t\t// set column widths\n\t\tapptTable.getColumnModel().getColumn(0).setPreferredWidth(125);\n\t\tapptTable.getColumnModel().getColumn(1).setPreferredWidth(75);\n\n\t\tListSelectionModel rowSM = apptTable.getSelectionModel();\n\t\trowSM.addListSelectionListener(this);\n\n\t\tTableColumnModel tcm = apptTable.getColumnModel();\n\t\tTableColumn column = tcm.getColumn(2);\n\t\ttcm.removeColumn(column);\n\n\t\tshowDate(year, month, day);\n\n\t}\n\n\t/**\n\t * Edit a copy of the selected appointment\n\t */\n\tprivate void copyAppt() {\n\t\tint[] keys = getSelectedKeys();\n\t\tif (keys.length != 1) {\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"select_appt\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// get date for new appt\n\t\tDateDialog dlg = new DateDialog(null);\n\t\tCalendar cal = new GregorianCalendar();\n\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\t\tdlg.setCalendar(cal);\n\t\tdlg.setVisible(true);\n\t\tCalendar dlgcal = dlg.getCalendar();\n\t\tif (dlgcal == null)\n\t\t\treturn;\n\n\t\tshowDate(dlgcal.get(Calendar.YEAR), dlgcal.get(Calendar.MONTH), dlgcal\n\t\t\t\t.get(Calendar.DATE));\n\t\tAppointment appt;\n\t\ttry {\n\t\t\tappt = AppointmentModel.getReference().getAppt(keys[0]);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif( appt.isEncrypted())\n\t\t{\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"no_copy_enc\"));\n\t\t\treturn;\n\t\t}\n\t\tappointmentPanel.showapp(-1, appt);\n\t\trefresh();\n\n\t}\n\n\t/**\n\t * Gets the copy button.\n\t * \n\t * @return the copy button\n\t */\n\tprivate JButton getCopyButton() {\n\t\tif (copyButton == null) {\n\t\t\tcopyButton = new JButton();\n\t\t\tResourceHelper.setText(copyButton, \"copy_appt\");\n\t\t\tcopyButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Copy16.gif\"));\n\t\t\tcopyButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tcopyAppt();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn copyButton;\n\t}\n\n\n\t/**\n\t * Gets the date chooser\n\t * \n\t * @return the date chooser\n\t */\n\tprivate JDateChooser getDateCB() {\n\t\tif (dateChooser == null) {\n\t\t\tdateChooser = new MyDateChooser();\n\t\t\t// cb.setCalendar(cal_);\n\t\t\tdateChooser.addPropertyChangeListener(\"date\",\n\t\t\t\t\tnew PropertyChangeListener() {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void propertyChange(PropertyChangeEvent arg0) {\n\t\t\t\t\t\t\tCalendar cal = dateChooser.getCalendar();\n\t\t\t\t\t\t\tif (cal != null) {\n\t\t\t\t\t\t\t\tshowDate(cal.get(Calendar.YEAR), cal\n\t\t\t\t\t\t\t\t\t\t.get(Calendar.MONTH), cal\n\t\t\t\t\t\t\t\t\t\t.get(Calendar.DATE));\n\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\t\tdateChooser.setCalendar(cal_);\n\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t}\n\n\t\treturn (dateChooser);\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn title_;\n\t}\n\t\n\t@Override\n\tprotected PrefName getFrameSizePref() {\n\t\treturn new PrefName(Resource.getResourceString(\"Appointment_Editor\") + \"_framesize\", \"-1,-1,800,600,N\");\n\t}\n\n\t/**\n\t * get the reminder button\n\t * \n\t * @return the reminder button\n\t */\n\tprivate JButton getReminderButton() {\n\t\tif (reminderButton == null) {\n\t\t\treminderButton = new JButton();\n\t\t\tResourceHelper.setText(reminderButton, \"send_reminder\");\n\t\t\treminderButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/ComposeMail16.gif\"));\n\t\t\treminderButton\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(\n\t\t\t\t\t\t\t\tjava.awt.event.ActionEvent evt) {\n\t\t\t\t\t\t\tclass MailThread extends Thread {\n\t\t\t\t\t\t\t\tprivate final Calendar cal;\n\t\t\t\t\t\t\t\tprivate final String passwd;\n\n\t\t\t\t\t\t\t\tpublic MailThread(Calendar c, String pass) {\n\t\t\t\t\t\t\t\t\tcal = c;\n\t\t\t\t\t\t\t\t\tthis.passwd = pass;\n\t\t\t\t\t\t\t\t\tthis.setName(\"Reminder Mail Thread\");\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\tpublic void run() {\n\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tEmailReminder\n\t\t\t\t\t\t\t\t\t\t\t\t.sendDailyEmailReminder(cal, passwd);\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\tfinal Exception fe = e;\n\t\t\t\t\t\t\t\t\t\tSwingUtilities\n\t\t\t\t\t\t\t\t\t\t\t\t.invokeLater(new Runnable() {\n\t\t\t\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(fe);\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tString pass = PasswordHelper.getReference().decryptText(Prefs.getPref(PrefName.EMAILPASS), \"Unlock Email Password\", false);\n\t\t\t\t\t\t\t\tnew MailThread(cal_, pass).start();\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t\treturn reminderButton;\n\t}\n\n\t/**\n\t * Gets the selected appointments.\n\t * \n\t * @return the selected appointments\n\t */\n\tprivate List<Appointment> getSelectedAppointments() {\n\t\tint[] keys = getSelectedKeys();\n\t\tList<Appointment> appts = new ArrayList<Appointment>(keys.length);\n\t\tAppointmentModel model = AppointmentModel.getReference();\n\t\tfor (int i = 0; i < keys.length; ++i) {\n\t\t\ttry {\n\t\t\t\tappts.add(model.getAppt(keys[i]));\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t\treturn appts;\n\t}\n\n\t/**\n\t * Gets the selected appt keys.\n\t * \n\t * @return the selected appt keys\n\t */\n\tprivate int[] getSelectedKeys() {\n\t\tint[] rows = apptTable.getSelectedRows();\n\t\tList<Integer> keyList = new ArrayList<Integer>();\n\t\tfor (int i = 0; i < rows.length; ++i) {\n\t\t\tint row = rows[i];\n\t\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\t\t\tkeyList.add((Integer) tm.getValueAt(row, 2));\n\t\t}\n\n\t\tint[] keys = new int[keyList.size()];\n\t\tfor (int i = 0; i < keyList.size(); ++i)\n\t\t\tkeys[i] = keyList.get(i).intValue();\n\n\t\treturn keys;\n\t}\n\n\t/**\n\t * init the ui components\n\t */\n\tprivate void initComponents()// GEN-BEGIN:initComponents\n\t{\n\n\t\tappointmentListPanel = new JPanel();\n\t\tapptTable = new JTable();\n\t\tbuttonPanel = new JPanel();\n\t\taddButton = new JButton();\n\t\tdeleteButton = new JButton();\n\t\tdeleteOneOnlyButton = new JButton();\n\n\t\tappointmentListPanel.setLayout(new GridBagLayout());\n\n\t\tappointmentListPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"apptlist\")));\n\n\t\tapptTable.setBorder(new LineBorder(new java.awt.Color(0, 0, 0)));\n\t\tapptTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);\n\t\tapptTable\n\t\t\t\t.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\n\t\tapptTable.setShowGrid(true);\n\t\tapptTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tbuttonPanel.setLayout(new java.awt.GridLayout(0, 1));\n\n\t\taddButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Edit16.gif\"));\n\t\tResourceHelper.setText(addButton, \"EditNew\");\n\t\taddButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tapptTable.clearSelection();\n\t\t\t\tappointmentPanel.showapp(-1, null);\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(addButton);\n\t\tbuttonPanel.add(getCopyButton());\n\n\t\tdeleteButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Delete16.gif\"));\n\t\tResourceHelper.setText(deleteButton, \"Delete\");\n\t\tdeleteButton.setToolTipText(Resource.getResourceString(\"del_tip\"));\n\n\t\tActionListener alDel = new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\n\t\t\t\tint[] keys = getSelectedKeys();\n\t\t\t\tif( keys.length == 0) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"no_appts_selected\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tint ret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t.getResourceString(\"Really_Delete_Apps\")\n\t\t\t\t\t\t+ \"?\", Resource\n\t\t\t\t\t\t.getResourceString(\"Confirm_Delete\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\treturn;\n\n\t\t\t\tAppointmentModel model = AppointmentModel.getReference();\n\t\t\t\tfor (int i = 0; i < keys.length; ++i)\n\t\t\t\t\tmodel.delAppt(keys[i]);\n\t\t\t\tapptTable.clearSelection();\n\t\t\t}\n\t\t};\n\t\tdeleteButton.addActionListener(alDel);\n\t\tbuttonPanel.add(deleteButton);\n\n\t\tdeleteOneOnlyButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Delete16.gif\"));\n\t\tResourceHelper.setText(deleteOneOnlyButton, \"Delete_One_Only\");\n\t\tdeleteOneOnlyButton.setToolTipText(Resource\n\t\t\t\t.getResourceString(\"doo_tip\"));\n\t\tActionListener alDelOne = new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tint[] keys = getSelectedKeys();\n\t\t\t\tif( keys.length == 0) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"no_appts_selected\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tAppointmentModel model = AppointmentModel.getReference();\n\t\t\t\tfor (int i = 0; i < keys.length; ++i)\n\t\t\t\t\tmodel.delOneOnly(keys[i], cal_.getTime());\n\t\t\t\tapptTable.clearSelection();\n\t\t\t}\n\t\t};\n\t\tdeleteOneOnlyButton.addActionListener(alDelOne);\n\n\t\tActionListener alMoveToFollowingDay = new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tonMoveToFollowingDay(getSelectedAppointments());\n\t\t\t}\n\t\t};\n\n\t\tActionListener alChangeDate = new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tonChangeDate(getSelectedAppointments());\n\t\t\t}\n\t\t};\n\n\t\tnew PopupMenuHelper(apptTable, new PopupMenuHelper.Entry[] {\n\t\t\t\tnew PopupMenuHelper.Entry(alDel, \"Delete\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alDelOne, \"Delete_One_Only\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alMoveToFollowingDay,\n\t\t\t\t\t\t\"Move_To_Following_Day\"),\n\t\t\t\tnew PopupMenuHelper.Entry(alChangeDate, \"changedate\"), });\n\n\t\tbuttonPanel.add(getReminderButton(), null);\n\t\tbuttonPanel.add(deleteOneOnlyButton, deleteOneOnlyButton.getName());\n\n\t\tappointmentListPanel.add(getDateCB(), GridBagConstraintsFactory.create(\n\t\t\t\t0, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tappointmentListPanel.add(apptTable, GridBagConstraintsFactory\n\t\t\t\t.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\t\tappointmentListPanel.add(buttonPanel, GridBagConstraintsFactory.create(\n\t\t\t\t0, 2));\n\n\t\tsetLayout(new GridBagLayout());\n\t\t\n\t\tadd(appointmentPanel, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\t\tadd(appointmentListPanel, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH));\n\n\t}\n\n\t\n\t@Override\n\t/**\n\t * reload the UI from the model\n\t */\n\tpublic void refresh() {\n\n\t\tint shownAppt = appointmentPanel.getCurrentlyShownAppointmentKey();\n\t\t\n\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tapptTable.getSelectionModel().removeListSelectionListener(this);\n\t\t\t\n\t\t\t// clear all table rows\n\t\t\ttm.setRowCount(0);\n\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\n\t\t\tString priv = Prefs.getPref(PrefName.SHOWPRIVATE);\n\t\t\t//String pub = Prefs.getPref(PrefName.SHOWPUBLIC);\n\n\t\t\tList<Integer> alist_ = AppointmentModel.getReference().getAppts(\n\t\t\t\t\tcal_.getTime());\n\t\t\tif (alist_ != null) {\n\t\t\t\tIterator<Integer> it = alist_.iterator();\n\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\tInteger key = it.next();\n\t\t\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\t\t\tkey.intValue());\n\n\t\t\t\t\tif (ap.isPrivate() && !priv.equals(\"true\"))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t//if (!ap.isPrivate() && !pub.equals(\"true\"))\n\t\t\t\t\t//\tcontinue;\n\n\t\t\t\t\tObject[] ro = new Object[3];\n\t\t\t\t\t\n\t\t\t\t\tif( ap.isEncrypted())\n\t\t\t\t\t\tro[0] = Resource.getResourceString(\"EncryptedItemShort\");\n\t\t\t\t\telse\n\t\t\t\t\t\tro[0] = ap.getText();\n\n\t\t\t\t\t// just get time\n\t\t\t\t\tDate d = ap.getDate();\n\t\t\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\t\t\tcal.setTime(d);\n\t\t\t\t\tcal.set(2000, 1, 1);\n\n\t\t\t\t\t// use second field to tell UI if the appt is untimed\n\t\t\t\t\tif (AppointmentModel.isNote(ap))\n\t\t\t\t\t\tcal.set(Calendar.SECOND, 0);\n\t\t\t\t\telse\n\t\t\t\t\t\tcal.set(Calendar.SECOND, 1);\n\n\t\t\t\t\tro[1] = cal.getTime();\n\t\t\t\t\tro[2] = key;\n\n\t\t\t\t\ttm.addRow(ro);\n\t\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// resize the table based on new row count\n\t\t\tint row = apptTable.getRowCount();\n\t\t\tapptTable.setPreferredSize(new Dimension(150, row * 16));\n\n\t\t\t// apply default sort to the table\n\t\t\tif (!tm.isSorted())\n\t\t\t\ttm.sortByColumn(1);\n\t\t\telse\n\t\t\t\ttm.sort();\n\t\t\t\t\t\t\t\n\t\t\tfor( int i = 0; i < tm.getRowCount(); i++)\n\t\t\t{\n\t\t\t\tint key = (int) tm.getValueAt(i, 2);\n\t\t\t\tif( key == shownAppt )\n\t\t\t\t{\n\t\t\t\t\tapptTable.getSelectionModel().setSelectionInterval(i, i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t} finally {\n\t\t\tapptTable.getSelectionModel().addListSelectionListener(this);\n\t\t}\n\n\n\t\t\n\t}\n\n\t/**\n\t * Show an appointment in the list and editor\n\t * \n\t * @param key\n\t *            the appt key\n\t */\n\tpublic void showApp(int key) {\n\t\tapptTable.clearSelection();\n\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\t\tint rows = tm.getRowCount();\n\t\tfor (int i = 0; i < rows; ++i) {\n\t\t\tInteger k = (Integer) tm.getValueAt(i, 2);\n\t\t\tif (key == k.intValue()) {\n\t\t\t\tapptTable.getSelectionModel().setSelectionInterval(i, i);\n\t\t\t}\n\t\t}\n\t\tappointmentPanel.showapp(key, null);\n\t}\n\n\t/**\n\t * set the UI to show appts for a particular date.\n\t * \n\t * @param year\n\t *            the year\n\t * @param month\n\t *            the month\n\t * @param day\n\t *            the day\n\t */\n\tprivate void showDate(int year, int month, int day) {\n\t\tcal_ = new GregorianCalendar(year, month, day);\n\t\tgetDateCB().setCalendar(cal_);\n\t\tDate d = cal_.getTime();\n\t\ttitle_ = Resource.getResourceString(\"Appointment_Editor_for_\") + \" \"\n\t\t\t\t+ DateFormat.getDateInstance(DateFormat.SHORT).format(d);\n\t\tupdateTitle();\n\n\t\t// clear all rows\n\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\t\ttm.setRowCount(0);\n\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\tappointmentPanel.setDate(year, month, day);\n\t\tappointmentPanel.showapp(-1, null);\n\n\t\trefresh();\n\t}\n\n\t/**\n\t * set the start time for the new appt in the appt editor.\n\t * \n\t * @param hour\n\t * \t\t\tthe start hour of the appointment\n\t * @param minute\n\t * \t\t\tthe start minute of the appointment\n\t */\n\tpublic void setTime(int hour, int minute) {\n\t\tappointmentPanel.setStartTime(hour, minute);\n\t\tappointmentPanel.showapp(-1, null);\n\t\trefresh();\n\t}\n\t\n\t/**\n\t * react to ListSelectionEvents\n\t */\n\t@Override\n\tpublic void valueChanged(ListSelectionEvent e) {\n\t\t// Ignore extra messages.\n\t\tif (e.getValueIsAdjusting())\n\t\t\treturn;\n\n\t\tListSelectionModel lsm = (ListSelectionModel) e.getSource();\n\t\tif (lsm.isSelectionEmpty()) {\n\t\t\tappointmentPanel.showapp(-1, null);\n\t\t\treturn;\n\t\t}\n\t\tint row = lsm.getMinSelectionIndex();\n\t\tif (row == -1)\n\t\t\treturn;\n\t\tTableSorter tm = (TableSorter) apptTable.getModel();\n\t\tInteger apptkey = (Integer) tm.getValueAt(row, 2);\n\n\t\tappointmentPanel.showapp(apptkey.intValue(), null);\n\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t\n\t\trefresh();\n\t\t\n\t\tif( event.getModel() instanceof AppointmentModel )\n\t\t{\n\t\t\tif( event.getObject() instanceof Appointment )\n\t\t\t{\n\t\t\t\tAppointment appt = (Appointment) event.getObject();\n\t\t\t\tif( appt.getKey() == appointmentPanel.getCurrentlyShownAppointmentKey())\n\t\t\t\t{\n\t\t\t\t\t// currently shown appt has changed, so show new\n\t\t\t\t\t// if may have moved to another day - not worth checking\n\t\t\t\t\tapptTable.clearSelection();\n\t\t\t\t\tappointmentPanel.showapp(-1, null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/AppointmentPanel.java",
    "content": "/*\n * This file is part of BORG. BORG is free software; you can redistribute it\n * and/or modify it under the terms of the GNU General Public License as\n * published by the Free Software Foundation; either version 2 of the License,\n * or (at your option) any later version. BORG is distributed in the hope that\n * it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details. You should have received a copy of\n * the GNU General Public License along with BORG; if not, write to the Free\n * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n * USA Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport com.toedter.calendar.JDateChooser;\nimport lombok.Getter;\nimport net.sf.borg.common.*;\nimport net.sf.borg.model.*;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.link.LinkPanel;\nimport net.sf.borg.ui.popup.PopupOptionsView;\nimport net.sf.borg.ui.popup.PopupOptionsView.PopupOptionsListener;\nimport net.sf.borg.ui.util.*;\n\nimport javax.swing.*;\nimport javax.swing.border.BevelBorder;\nimport javax.swing.border.TitledBorder;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\nimport static net.sf.borg.model.Theme.*;\n\n/**\n * AppointmentPanel is the UI for editing an Appointment.\n */\npublic class AppointmentPanel extends JPanel implements PopupOptionsListener, Model.Listener {\n\n    private static final long serialVersionUID = 1L;\n\n    private static final Integer DEFAULT_PRIORITY = Integer.valueOf(5);\n\n\n    static private final SpinnerNumberModel prioritySpinnerModel = new SpinnerNumberModel(5, 1, 10, 1);\n\n    // appt text area\n    private JTextArea appointmentBodyTextArea;\n\n    // appt title field\n    private JTextField apptTitleField = null;\n\n    // link panel\n    private LinkPanel linkPanel = null;\n\n    // category combo box\n    private JComboBox<String> categoryBox;\n\n    // priority chooser\n    private JSpinner prioritySpinner;\n\n    // date change checkbox\n    private JCheckBox dateChangeCheckBox;\n\n    // color select combo box\n    private final ColorComboBox colorComboBox = new ColorComboBox();\n\n\n    // reminder times\n    private char[] custRemTimes;\n\n    // date being shown\n    private int day_;\n    private int month_;\n    private int year_;\n\n    // start time for double-clicked appointments\n    private int starthour;\n    private int startminute;\n\n    // the seven toggle buttons for selecting repeat days for a repeat\n    // type of select days\n    private final JToggleButton[] dayToggles = new JToggleButton[7];\n\n    // the panel containing the day toggle buttons\n    private JPanel selectDayButtonPanel = null;\n\n    // repeat frequency combo box\n    private JComboBox<String> repeatFrequencyComboBox;\n\n    // half-day (vacation) check box\n    private JCheckBox halfDayVacationCheckBox;\n\n    // holiday check box\n    private JCheckBox holidayCheckBox;\n\n    // currently shown appt key\n    @Getter\n    private int currentlyShownAppointmentKey;\n\n    // number of days spinner for N days repeat\n    private JSpinner nTimesValue = null;\n\n    // indicator that a new appt is being edited\n    private JLabel newAppointmentIndicatorLabel;\n\n    // new date chooser\n    private JDateChooser newdatefield;\n\n    // untimed appt checkbox\n    private JCheckBox untimedCheckBox;\n\n    // label that shows current popup times\n    private JLabel popupTimesLabel;\n\n    // private check box\n    private JCheckBox privateCheckBox;\n\n    // show repeat number check box\n    private JCheckBox showRepeatNumberCheckBox = null;\n\n    // number fo repeats spinner\n    private JSpinner numberOfRepeatsSpinner;\n\n    private DateTimePanel startTimePanel = null;\n    private DateTimePanel endTimePanel = null;\n\n    // todo check box\n    private JCheckBox todoCheckBox;\n\n    // vacation check box\n    private JCheckBox vacationCheckBox;\n\n    /**\n     * decrypt button\n     */\n    private JButton decryptButton = null;\n\n    private final JLabel durationLabel = new JLabel();\n\n    /**\n     * encryption checkbox\n     */\n    private JCheckBox encryptBox = null;\n\n    /**\n     * repeat until date chooser\n     */\n    private JDateChooser untilDate = null;\n\n    /**\n     * radio buttons to choose between repeat forever, times, and until date\n     */\n    private JRadioButton repeatForeverRadio = null;\n    private JRadioButton repeatTimesRadio = null;\n    private JRadioButton repeatUntilRadio = null;\n\n    /*\n     * save buttons\n     */\n    private JButton saveCloseButton = null;\n    private JButton saveButton = null;\n\n    /**\n     * Instantiates a new appointment panel.\n     *\n     * @param year  the year\n     * @param month the month\n     * @param day   the day\n     */\n    public AppointmentPanel(int year, int month, int day) {\n\n        // init GUI\n        initComponents();\n\n        decryptButton.setEnabled(false);\n\n        // set up priority pull down\n        prioritySpinner.setModel(prioritySpinnerModel);\n        prioritySpinner.setVisible(true);\n\n        // load categories\n        try {\n            Collection<String> cats = CategoryModel.getReference().getCategories();\n            Iterator<String> it = cats.iterator();\n            while (it.hasNext()) {\n                categoryBox.addItem(it.next());\n            }\n            categoryBox.setSelectedIndex(0);\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n\n        setDate(year, month, day);\n        setCustRemTimes(null);\n        apptTitleField.requestFocus();\n\n        // if the start time is changed, then set the end time to start plus 30\n        // minutes\n        startTimePanel.addTimeListener(new ActionListener() {\n\n            @Override\n            public void actionPerformed(ActionEvent arg0) {\n                try {\n                    Date s = startTimePanel.getTime();\n                    endTimePanel.setTime(new Date(s.getTime() + 1000 * 60 * 30));\n                    durationLabel.setText(DateUtil.minuteString(30));\n                } catch (Warning e) {\n                    // nothing\n                }\n\n            }\n\n        });\n\n        endTimePanel.addTimeListener(new ActionListener() {\n\n            @Override\n            public void actionPerformed(ActionEvent arg0) {\n                try {\n                    int mins = (int) ((endTimePanel.getTime().getTime() - startTimePanel.getTime().getTime())\n                            / (60 * 1000));\n                    if (mins < 0)\n                        mins += (24 * 60);\n                    else if (mins >= 24 * 60)\n                        mins -= (24 * 60);\n                    durationLabel.setText(DateUtil.minuteString(mins));\n                } catch (Warning e) {\n                    // nothing\n                }\n\n            }\n\n        });\n\n        CategoryModel.getReference().addListener(this);\n    }\n\n    /**\n     * encrypt the text of a given appointment - and prompt for password too\n     *\n     * @throws Exception\n     */\n    private static void encryptAppt(Appointment appt) throws Exception {\n        String pw = PasswordHelper.getReference().getEncryptionKeyWithTimeout(\"Encrypt Appointment\");\n        if (pw == null)\n            throw new Exception(\"Cannot encrypt appointment without keystore password\");\n        appt.encrypt(pw);\n\n    }\n\n    /**\n     * add an appointment to the model based on the UI settings.\n     *\n     * @throws Warning\n     * @throws Exception\n     */\n    private void add_appt() throws Warning, Exception {\n\n        // get a new appt from the model and set it from the user data\n        AppointmentModel calmod_ = AppointmentModel.getReference();\n        Appointment r = calmod_.newAppt();\n        setAppt(r, true);\n\n        // encrypt it\n        if (encryptBox.isSelected()) {\n            encryptAppt(r);\n        }\n\n        // save it\n        calmod_.saveAppt(r);\n\n        // clear UI and show a new appt\n        showapp(-1, null);\n    }\n\n    /**\n     * update an appointment in the model based on UI settings\n     *\n     * @throws Warning\n     * @throws Exception\n     */\n    private void chg_appt() throws Warning, Exception {\n\n        // get a new empty appt from the model and set it using the data the\n        // user has entered\n        AppointmentModel calmod_ = AppointmentModel.getReference();\n        Appointment appt = calmod_.newAppt();\n        boolean dateChg = setAppt(appt, true);\n        appt.setKey(currentlyShownAppointmentKey);\n\n        // call the model to change the appt\n        if (dateChg == false) {\n\n            // need to selectively preserve some data from original appt\n            try {\n\n                // date of orig might not match appt panel date - so\n                // we must keep the original date - we may be editing a repeat\n                Appointment originalAppt = calmod_.getAppt(currentlyShownAppointmentKey);\n\n                Calendar origDate = new GregorianCalendar();\n                origDate.setTime(originalAppt.getDate());\n\n                // preserve the day, but change the time in case we are editing\n                // one of the repeat\n                // occurrences and not the first occurrence\n                Calendar newDate = new GregorianCalendar();\n                newDate.setTime(appt.getDate());\n                newDate.set(Calendar.YEAR, origDate.get(Calendar.YEAR));\n                newDate.set(Calendar.MONTH, origDate.get(Calendar.MONTH));\n                newDate.set(Calendar.DATE, origDate.get(Calendar.DATE));\n                appt.setDate(newDate.getTime());\n\n                appt.setCreateTime(originalAppt.getCreateTime());\n                appt.setLastMod(originalAppt.getLastMod());\n                appt.setUid(originalAppt.getUid());\n                appt.setUrl(originalAppt.getUrl());\n\n                // determine if we can keep certain fields related to repeating\n                // and todos\n                if (appt.getTimes().intValue() == originalAppt.getTimes().intValue()\n                        && appt.getRepeatUntil() == originalAppt.getRepeatUntil() && appt.getFrequency() != null\n                        && originalAppt.getFrequency() != null\n                        && Repeat.getFreq(appt.getFrequency()).equals(Repeat.getFreq(originalAppt.getFrequency()))\n                        && appt.isTodo() == originalAppt.isTodo()\n                        && appt.isRepeatFlag() == originalAppt.isRepeatFlag()) {\n\n                    // we can keep skip list and next todo since the repeat/todo\n                    // info is not changing\n                    // otherwise, we'd reset these\n                    appt.setSkipList(originalAppt.getSkipList());\n                    appt.setNextTodo(originalAppt.getNextTodo());\n                }\n\n            } catch (Exception e) {\n                // empty\n            }\n\n            // encrypt it\n            if (encryptBox.isSelected()) {\n                encryptAppt(appt);\n            }\n            calmod_.saveAppt(appt);\n        } else {\n            // for a date change - everything comes from the screen\n            // the old appt info is tossed out - except for caldav UID\n\n            Appointment originalAppt = calmod_.getAppt(currentlyShownAppointmentKey);\n            appt.setUid(originalAppt.getUid());\n            appt.setUrl(originalAppt.getUrl());\n\n            appt.setKey(currentlyShownAppointmentKey);\n            try {\n                // encrypt it\n                if (encryptBox.isSelected()) {\n                    encryptAppt(appt);\n                }\n                calmod_.saveAppt(appt);\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().errmsg(e);\n            }\n\n        }\n\n        // show a new appt\n        showapp(-1, null);\n    }\n\n    /**\n     * This method initializes the day select toggle button panel\n     *\n     * @return the day select toggle button panel\n     */\n    private JPanel createDaySelectPanel() {\n\n        selectDayButtonPanel = new JPanel();\n\n        // set up toggle buttons\n        SimpleDateFormat shortDayFmt = new SimpleDateFormat(\"EEE\");\n        GregorianCalendar tmpcal = new GregorianCalendar();\n        tmpcal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);\n        for (int i = 0; i < dayToggles.length; i++) {\n            dayToggles[i] = new JToggleButton();\n            dayToggles[i].setText(shortDayFmt.format(tmpcal.getTime()));\n            selectDayButtonPanel.add(dayToggles[i]);\n            tmpcal.add(Calendar.DAY_OF_WEEK, 1);\n        }\n        return selectDayButtonPanel;\n    }\n\n    /**\n     * create the repeat panel\n     *\n     * @return the repear panel\n     */\n    private JPanel createRepeatPanel() {\n\n        JPanel theRepeatPanel = new JPanel();\n        theRepeatPanel.setLayout(new GridBagLayout());\n\n        theRepeatPanel.setBorder(new TitledBorder(null, Resource.getResourceString(\"Recurrence\"),\n                TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));\n\n        JLabel frequencyLabel = new JLabel();\n        frequencyLabel.setHorizontalAlignment(SwingConstants.RIGHT);\n        ResourceHelper.setText(frequencyLabel, \"Frequency\");\n        frequencyLabel.setLabelFor(repeatFrequencyComboBox);\n        theRepeatPanel.add(frequencyLabel, GridBagConstraintsFactory.create(0, 0));\n\n        // load repeat frequency strings\n        repeatFrequencyComboBox = new JComboBox<String>();\n        repeatFrequencyComboBox.setOpaque(false);\n        for (int i = 0; ; i++) {\n            String fs = Repeat.getFreqString(i);\n            if (fs == null)\n                break;\n            repeatFrequencyComboBox.addItem(fs);\n        }\n        repeatFrequencyComboBox.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n                // show/hide the various repeat widgets based on the\n                // chosen\n                // frequency\n                timesEnable();\n            }\n\n        });\n        theRepeatPanel.add(repeatFrequencyComboBox,\n                GridBagConstraintsFactory.create(1, 0, GridBagConstraints.HORIZONTAL));\n\n        ButtonGroup buttonGroup = new ButtonGroup();\n\n        repeatTimesRadio = new JRadioButton();\n        ResourceHelper.setText(repeatTimesRadio, \"Times\");\n        theRepeatPanel.add(repeatTimesRadio, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n        buttonGroup.add(repeatTimesRadio);\n\n        numberOfRepeatsSpinner = new JSpinner();\n        theRepeatPanel.add(numberOfRepeatsSpinner, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH));\n        SpinnerNumberModel mod = (SpinnerNumberModel) numberOfRepeatsSpinner.getModel();\n        mod.setMinimum(Integer.valueOf(1));\n        mod.setMaximum(Integer.valueOf(99999));\n\n        repeatUntilRadio = new JRadioButton();\n        ResourceHelper.setText(repeatUntilRadio, \"Until\");\n        theRepeatPanel.add(repeatUntilRadio, GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n        buttonGroup.add(repeatUntilRadio);\n\n        untilDate = new MyDateChooser(new PlainDateEditor());\n        theRepeatPanel.add(untilDate, GridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH));\n\n        nTimesValue = new JSpinner();\n        nTimesValue.setModel(new SpinnerNumberModel(2, 2, 3000, 1));\n        theRepeatPanel.add(nTimesValue, GridBagConstraintsFactory.create(2, 0, GridBagConstraints.HORIZONTAL));\n\n        repeatForeverRadio = new JRadioButton();\n        ResourceHelper.setText(repeatForeverRadio, \"forever\");\n\n        theRepeatPanel.add(repeatForeverRadio, GridBagConstraintsFactory.create(2, 1, GridBagConstraints.BOTH));\n\n        buttonGroup.add(repeatForeverRadio);\n\n        showRepeatNumberCheckBox = new JCheckBox();\n        ResourceHelper.setText(showRepeatNumberCheckBox, \"show_rpt_num\");\n        theRepeatPanel.add(showRepeatNumberCheckBox, GridBagConstraintsFactory.create(2, 2, GridBagConstraints.BOTH));\n\n        GridBagConstraints gridBagConstraints1 = new GridBagConstraints();\n        gridBagConstraints1.fill = GridBagConstraints.BOTH;\n        gridBagConstraints1.gridy = 0;\n        gridBagConstraints1.gridx = 2;\n        gridBagConstraints1.gridwidth = 2;\n        theRepeatPanel.add(createDaySelectPanel(), gridBagConstraints1);\n\n        return theRepeatPanel;\n    }\n\n    /**\n     * initialize many of the UI components. This started as very very bad\n     * generated code. It's been cleaned up somewhat\n     */\n    private void initComponents() {\n\n        // lots of components are created here. This is how the code-gen lumped\n        // them.\n        // not worth it to move them all where they are used.\n        JLabel subjectLabel = new JLabel();\n        subjectLabel.setText(Resource.getResourceString(\"subject\"));\n        newAppointmentIndicatorLabel = new JLabel();\n        JScrollPane apptTextScroll = new JScrollPane();\n        appointmentBodyTextArea = new JTextArea(new LimitDocument(Prefs.getIntPref(PrefName.MAX_TEXT_SIZE)));\n        prioritySpinner = new JSpinner();\n        JLabel starttimeLabel = new JLabel();\n        JLabel endTimeLabel = new JLabel();\n        untimedCheckBox = new JCheckBox();\n        JLabel newDateLabel = new JLabel();\n        newdatefield = new JDateChooser(new PlainDateEditor());\n        newdatefield.setDateFormatString(\"MMM dd, yyyy\");\n        dateChangeCheckBox = new JCheckBox();\n        JPanel appointmentPropetiesPanel = new JPanel();\n        todoCheckBox = new JCheckBox();\n        vacationCheckBox = new JCheckBox();\n        halfDayVacationCheckBox = new JCheckBox();\n        holidayCheckBox = new JCheckBox();\n        privateCheckBox = new JCheckBox();\n        JLabel lblColor = new JLabel();\n        categoryBox = new JComboBox<String>();\n        JLabel lblCategory = new JLabel();\n        JLabel lblPriority = new JLabel();\n        JPanel buttonPanel = new JPanel();\n        saveButton = new JButton();\n        saveCloseButton = new JButton();\n        JButton savedefaultsbutton = new JButton();\n        apptTitleField = new JTextField();\n\n        lblCategory.setLabelFor(categoryBox);\n        lblColor.setLabelFor(colorComboBox);\n        newDateLabel.setLabelFor(newdatefield);\n\n        setLayout(new GridBagLayout());\n\n        newAppointmentIndicatorLabel.setForeground(java.awt.Color.red);\n\n        add(newAppointmentIndicatorLabel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n        // ********************************************************************\n        // appt text panel\n        // ********************************************************************\n        JPanel appointmentTextPanel = new JPanel();\n        appointmentTextPanel.setLayout(new GridBagLayout());\n        appointmentTextPanel.setBorder(new TitledBorder(Resource.getResourceString(\"appttext\")));\n        appointmentBodyTextArea.setColumns(40);\n        appointmentBodyTextArea.setLineWrap(true);\n        appointmentBodyTextArea.setRows(5);\n        appointmentBodyTextArea.setWrapStyleWord(true);\n        appointmentBodyTextArea.setBorder(new BevelBorder(BevelBorder.LOWERED));\n        appointmentBodyTextArea.setMinimumSize(new java.awt.Dimension(284, 140));\n        apptTextScroll.setViewportView(appointmentBodyTextArea);\n\n        appointmentTextPanel.add(subjectLabel, GridBagConstraintsFactory.create(0, 0));\n        appointmentTextPanel.add(apptTitleField,\n                GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n        appointmentTextPanel.add(apptTextScroll,\n                GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH, 0.5, 0.5));\n\n        GridBagConstraints gridBagConstraints3 = GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH);\n        gridBagConstraints3.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n        gridBagConstraints3.weightx = 1.5;\n        gridBagConstraints3.weighty = 2.0;\n        add(appointmentTextPanel, gridBagConstraints3);\n\n        // ********************************************************************\n        // appointment time panel\n        // ********************************************************************\n        JPanel appointmentTimePanel = new JPanel();\n        appointmentTimePanel.setLayout(new java.awt.GridBagLayout());\n        appointmentTimePanel.setBorder(new TitledBorder(Resource.getResourceString(\"appttime\")));\n\n        ResourceHelper.setText(starttimeLabel, \"Start_Time:\");\n        appointmentTimePanel.add(starttimeLabel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n        startTimePanel = new DateTimePanel(false, Prefs.getBoolPref(PrefName.MILTIME));\n        appointmentTimePanel.add(startTimePanel, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH));\n\n        ResourceHelper.setText(untimedCheckBox, \"No_Specific_Time\");\n        untimedCheckBox.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                untimedCheckBoxActionPerformed();\n            }\n        });\n        GridBagConstraints gridBagConstraints12 = GridBagConstraintsFactory.create(2, 0, GridBagConstraints.BOTH);\n\n        appointmentTimePanel.add(untimedCheckBox, gridBagConstraints12);\n\n        endTimeLabel.setText(Resource.getResourceString(\"EndTime\") + \":\");\n        appointmentTimePanel.add(endTimeLabel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n        endTimePanel = new DateTimePanel(false, Prefs.getBoolPref(PrefName.MILTIME));\n        appointmentTimePanel.add(endTimePanel, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH));\n\n        durationLabel.setText(\"1/2 hour\");\n\n        appointmentTimePanel.add(durationLabel, GridBagConstraintsFactory.create(2, 1, GridBagConstraints.BOTH));\n\n        ResourceHelper.setText(dateChangeCheckBox, \"changedate\");\n        dateChangeCheckBox.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                newdatefield.setEnabled(dateChangeCheckBox.isSelected());\n            }\n        });\n\n        appointmentTimePanel.add(new JLabel(\"       \"),\n                GridBagConstraintsFactory.create(3, 0, GridBagConstraints.HORIZONTAL));\n\n        appointmentTimePanel.add(dateChangeCheckBox,\n                GridBagConstraintsFactory.create(4, 0, GridBagConstraints.HORIZONTAL));\n\n        ResourceHelper.setText(newDateLabel, \"newDate:\");\n        appointmentTimePanel.add(newDateLabel, GridBagConstraintsFactory.create(5, 0, GridBagConstraints.BOTH));\n\n        appointmentTimePanel.add(newdatefield, GridBagConstraintsFactory.create(6, 0, GridBagConstraints.BOTH));\n\n        // ********************************************************************\n        // appt properties panel\n        // ********************************************************************\n        appointmentPropetiesPanel.setLayout(new GridBagLayout());\n\n        appointmentPropetiesPanel.setBorder(new TitledBorder(Resource.getResourceString(\"Properties\")));\n        appointmentPropetiesPanel.setMinimumSize(new java.awt.Dimension(539, 128));\n\n        ResourceHelper.setText(todoCheckBox, \"To_Do\");\n        todoCheckBox.setOpaque(false);\n\n        appointmentPropetiesPanel.add(todoCheckBox,\n                GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        vacationCheckBox.setForeground(new java.awt.Color(0, 102, 0));\n        ResourceHelper.setText(vacationCheckBox, \"Vacation\");\n        vacationCheckBox.setOpaque(false);\n\n        appointmentPropetiesPanel.add(vacationCheckBox,\n                GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        halfDayVacationCheckBox.setForeground(new java.awt.Color(0, 102, 102));\n        ResourceHelper.setText(halfDayVacationCheckBox, \"Half_Day\");\n        halfDayVacationCheckBox.setOpaque(false);\n\n        appointmentPropetiesPanel.add(halfDayVacationCheckBox,\n                GridBagConstraintsFactory.create(2, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        ResourceHelper.setText(holidayCheckBox, \"Holiday\");\n        holidayCheckBox.setOpaque(false);\n\n        appointmentPropetiesPanel.add(holidayCheckBox,\n                GridBagConstraintsFactory.create(3, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        ResourceHelper.setText(privateCheckBox, \"Private\");\n        privateCheckBox.setOpaque(false);\n\n        appointmentPropetiesPanel.add(privateCheckBox,\n                GridBagConstraintsFactory.create(4, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        JPanel subPanel = new JPanel();\n        subPanel.setLayout(new GridBagLayout());\n\n        subPanel.add(new JLabel(),\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH, 1.0, 0.0)); // spacer\n\n        lblColor.setHorizontalAlignment(SwingConstants.RIGHT);\n        ResourceHelper.setText(lblColor, \"Color\");\n\n        subPanel.add(lblColor,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH));\n\n\n        subPanel.add(colorComboBox,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH));\n\n\n        lblCategory.setHorizontalAlignment(SwingConstants.RIGHT);\n        ResourceHelper.setText(lblCategory, \"Category\");\n        subPanel.add(lblCategory,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n        subPanel.add(categoryBox,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH));\n\n        lblPriority.setHorizontalAlignment(SwingConstants.RIGHT);\n        ResourceHelper.setText(lblPriority, \"Priority\");\n        subPanel.add(lblPriority,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n        subPanel.add(prioritySpinner,\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH));\n\n        subPanel.add(new JLabel(),\n                GridBagConstraintsFactory.create(GridBagConstraints.RELATIVE, 0, GridBagConstraints.BOTH, 1.0, 0.0)); // spacer\n\n        GridBagConstraints subPanelConstraints = GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0,\n                0.0);\n        subPanelConstraints.gridwidth = 5;\n        appointmentPropetiesPanel.add(subPanel, subPanelConstraints);\n\n        // ********************************************************************\n        // button panel\n        // ********************************************************************\n\n        saveButton.setIcon(IconHelper.getIcon(\"/resource/Save16.gif\"));\n        ResourceHelper.setText(saveButton, \"Save\");\n        saveButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                try {\n                    if (currentlyShownAppointmentKey == -1) {\n                        add_appt();\n                    } else {\n                        chg_appt();\n                    }\n                } catch (Warning w) {\n                    Errmsg.getErrorHandler().notice(w.getMessage());\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                }\n            }\n        });\n        buttonPanel.add(saveButton);\n\n        saveCloseButton.setIcon(IconHelper.getIcon(\"/resource/Save16.gif\"));\n        ResourceHelper.setText(saveCloseButton, \"Save_&_Close\");\n        saveCloseButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                try {\n                    if (currentlyShownAppointmentKey == -1) {\n                        add_appt();\n                    } else {\n                        chg_appt();\n                    }\n                } catch (Warning w) {\n                    Errmsg.getErrorHandler().notice(w.getMessage());\n                    return;\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n\n                DockableView parent = DockableView.findDockableParent(saveCloseButton);\n                if (parent != null)\n                    parent.close();\n            }\n        });\n        buttonPanel.add(saveCloseButton);\n\n        encryptBox = new JCheckBox();\n        encryptBox.setText(Resource.getResourceString(\"EncryptOnSave\"));\n        buttonPanel.add(encryptBox, null);\n\n        decryptButton = new JButton();\n        decryptButton.setText(Resource.getResourceString(\"decrypt\"));\n        decryptButton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent e) {\n                try {\n                    Appointment appt = AppointmentModel.getReference().getAppt(currentlyShownAppointmentKey);\n                    if (appt == null)\n                        return;\n                    String pw = PasswordHelper.getReference().getEncryptionKeyWithTimeout(\"Decrypt Appointment\");\n                    if (pw == null)\n                        return;\n                    appt.decrypt(pw);\n\n                    \n                    appointmentBodyTextArea.setText(appt.getBody());\n                    apptTitleField.setText(appt.getTitle());\n\n                    appointmentBodyTextArea.setEditable(true);\n                    apptTitleField.setEditable(true);\n\n                    decryptButton.setEnabled(false);\n\n                    saveButton.setEnabled(true);\n                    saveCloseButton.setEnabled(true);\n\n                } catch (Exception e1) {\n                    Errmsg.getErrorHandler().errmsg(e1);\n                }\n\n            }\n        });\n        buttonPanel.add(decryptButton, null);\n\n        savedefaultsbutton.setIcon(IconHelper.getIcon(\"/resource/SaveAs16.gif\"));\n        ResourceHelper.setText(savedefaultsbutton, \"save_Def\");\n        savedefaultsbutton.setToolTipText(Resource.getResourceString(\"sd_tip\"));\n        savedefaultsbutton.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                Appointment appt = new Appointment();\n                try {\n                    setAppt(appt, false);\n                } catch (Exception e) {\n                    Errmsg.getErrorHandler().errmsg(e);\n                    return;\n                }\n\n                AppointmentModel.getReference().saveDefaultAppointment(appt);\n            }\n        });\n\n        // add a spacer\n        buttonPanel.add(new JLabel(\"          \"));\n        buttonPanel.add(savedefaultsbutton);\n\n        // ********************************************************************\n        // popup reminders panel\n        // ********************************************************************\n        popupTimesLabel = new JLabel();\n        popupTimesLabel.setHorizontalAlignment(SwingConstants.RIGHT);\n        JButton popupTimesBtn = new JButton();\n        ResourceHelper.setText(popupTimesBtn, \"Change\");\n        final AppointmentPanel thisPanel = this;\n        popupTimesBtn.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                String apptTitle = apptTitleField.getText();\n                if (apptTitle.equals(\"\")) {\n                    apptTitle = Resource.getResourceString(\"*****_NEW_APPT_*****\");\n                }\n                PopupOptionsView pv = new PopupOptionsView(new String(custRemTimes), apptTitle, thisPanel);\n                pv.setVisible(true);\n            }\n        });\n\n        JPanel popupReminderPanel = new JPanel();\n        popupReminderPanel.setBorder(new TitledBorder(Resource.getResourceString(\"popup_reminders\")));\n        popupReminderPanel.add(popupTimesLabel);\n        popupReminderPanel.add(popupTimesBtn);\n\n        // ********************************************************************\n        // add panels to the top level\n        // ********************************************************************\n        GridBagConstraints gridBagConstraints17 = GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH, 1.0,\n                1.0);\n        gridBagConstraints17.gridwidth = GridBagConstraints.REMAINDER;\n        this.add(appointmentTimePanel, gridBagConstraints17);\n        this.setSize(648, 590);\n\n        GridBagConstraints gridBagConstraints38 = GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH);\n        gridBagConstraints38.gridwidth = 2;\n        this.add(appointmentPropetiesPanel, gridBagConstraints38);\n\n        this.add(createRepeatPanel(), GridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\n        GridBagConstraints gridBagConstraints91 = GridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH);\n        gridBagConstraints91.gridwidth = 2;\n        this.add(buttonPanel, gridBagConstraints91);\n\n        GridBagConstraints gridBagConstraints87 = GridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH, 9.0,\n                1.0);\n        gridBagConstraints87.gridwidth = 2;\n        this.add(popupReminderPanel, gridBagConstraints87);\n\n        linkPanel = new LinkPanel();\n        linkPanel.setBorder(new TitledBorder(Resource.getResourceString(\"links\")));\n        this.add(linkPanel, GridBagConstraintsFactory.create(1, 5, GridBagConstraints.BOTH));\n\n    }\n\n    /**\n     * enable/disable the time selection components based on the value of the\n     * untimed checkbox if untimed is selected, the time components are disabled\n     */\n    private void untimedCheckBoxActionPerformed() {\n        if (untimedCheckBox.isSelected()) {\n            startTimePanel.setTime(DateUtil.setToMidnight(new Date()));\n            startTimePanel.setEnabled(false);\n            endTimePanel.setTime(DateUtil.setToMidnight(new Date()));\n            endTimePanel.setEnabled(false);\n        } else {\n            startTimePanel.setEnabled(true);\n            endTimePanel.setEnabled(true);\n        }\n    }\n\n    /**\n     * Set the data in an Appointment from the UI values\n     *\n     * @param appt     the appointment\n     * @param validate validate the data\n     * @return true, if the date is changing\n     * @throws Warning\n     * @throws Exception\n     */\n    private boolean setAppt(Appointment appt, boolean validate) throws Warning, Exception {\n\n        // see if date is changing\n        Date nd = null;\n        boolean dateChg = false;\n        if (dateChangeCheckBox.isSelected()) {\n            nd = newdatefield.getDate();\n            if (nd != null)\n                dateChg = true;\n        }\n\n        Date d = startTimePanel.getTime();\n        GregorianCalendar g = new GregorianCalendar();\n        g.setTime(d);\n        // compute new date/time\n        if (nd == null) {\n            g.set(Calendar.YEAR, year_);\n            g.set(Calendar.MONTH, month_);\n            g.set(Calendar.DATE, day_);\n        } else {\n            GregorianCalendar g2 = new GregorianCalendar();\n            g2.setTime(nd);\n            g.set(Calendar.YEAR, g2.get(Calendar.YEAR));\n            g.set(Calendar.MONTH, g2.get(Calendar.MONTH));\n            g.set(Calendar.DATE, g2.get(Calendar.DATE));\n        }\n        appt.setDate(g.getTime());\n\n        // set untimed\n        if (untimedCheckBox.isSelected())\n            appt.setUntimed(\"Y\");\n\n        // force end time to be within 24 hrs of start\n        // ignore days in calculation\n        long end = endTimePanel.getTime().getTime() % 86400000L;\n        long beg = startTimePanel.getTime().getTime() % 86400000L;\n\n        int du = (int) (end - beg) / (1000 * 60);\n        if (du < 0)\n            du += (24 * 60);\n\n        if (du != 0)\n            appt.setDuration(Integer.valueOf(du));\n\n        // appointment text of some sort is required if we are validating\n        if (apptTitleField.getText().trim().isEmpty() && validate) {\n            apptTitleField.requestFocus();\n            throw new Warning(Resource.getResourceString(\"Please_enter_some_appointment_text\"));\n        }\n\n        // set text. add newline between title and body text\n        String t = apptTitleField.getText();\n        if (appointmentBodyTextArea.getText() != null && !appointmentBodyTextArea.getText().equals(\"\"))\n            t += \"\\n\" + appointmentBodyTextArea.getText();\n        appt.setText(t);\n\n        // to do\n        appt.setTodo(todoCheckBox.isSelected());\n\n        if (vacationCheckBox.isSelected() && halfDayVacationCheckBox.isSelected()) {\n            throw new Warning(Resource.getResourceString(\"vacation_warning\"));\n        }\n\n        // vacation, half-day, and private checkboxes\n        if (vacationCheckBox.isSelected())\n            appt.setVacation(Integer.valueOf(1));\n        if (halfDayVacationCheckBox.isSelected())\n            appt.setVacation(Integer.valueOf(2));\n        if (holidayCheckBox.isSelected())\n            appt.setHoliday(Integer.valueOf(1));\n\n        // private\n        appt.setPrivate(privateCheckBox.isSelected());\n\n        // color\n        String colorString = (String) colorComboBox.getSelectedItem();\n        if (colorString.equals(\"chosen\"))\n            appt.setColor(Integer.toString(colorComboBox.getChosenColor().getRGB()));\n        else\n            appt.setColor(colorString);\n\n        // repeat frequency\n        if (repeatFrequencyComboBox.getSelectedIndex() != 0) {\n            ArrayList<Integer> daylist = new ArrayList<Integer>();\n            if (dayToggles[0].isSelected())\n                daylist.add(Integer.valueOf(Calendar.SUNDAY));\n            if (dayToggles[1].isSelected())\n                daylist.add(Integer.valueOf(Calendar.MONDAY));\n            if (dayToggles[2].isSelected())\n                daylist.add(Integer.valueOf(Calendar.TUESDAY));\n            if (dayToggles[3].isSelected())\n                daylist.add(Integer.valueOf(Calendar.WEDNESDAY));\n            if (dayToggles[4].isSelected())\n                daylist.add(Integer.valueOf(Calendar.THURSDAY));\n            if (dayToggles[5].isSelected())\n                daylist.add(Integer.valueOf(Calendar.FRIDAY));\n            if (dayToggles[6].isSelected())\n                daylist.add(Integer.valueOf(Calendar.SATURDAY));\n            if (!Repeat.isCompatible(g, (String) repeatFrequencyComboBox.getSelectedItem(), daylist)) {\n                throw new Warning(Resource.getResourceString(\"recur_compat\"));\n            }\n            appt.setFrequency(Repeat.freqString((String) repeatFrequencyComboBox.getSelectedItem(),\n                    (Integer) nTimesValue.getValue(), showRepeatNumberCheckBox.isSelected(), daylist));\n        }\n\n        // repeat times\n        Integer tm = null;\n        if (repeatForeverRadio.isSelected()) {\n            tm = Integer.valueOf(Repeat.MAGIC_RPT_FOREVER_VALUE);\n        } else {\n            tm = (Integer) numberOfRepeatsSpinner.getValue();\n        }\n\n        appt.setRepeatFlag(false);\n        if (tm.intValue() > 1 && repeatFrequencyComboBox.getSelectedIndex() != 0) {\n            try {\n                appt.setTimes(tm);\n\n                if (tm.intValue() > 1)\n                    appt.setRepeatFlag(true);\n\n            } catch (Exception e) {\n                throw new Exception(Resource.getResourceString(\"Could_not_parse_times:_\") + tm);\n            }\n        } else {\n            appt.setTimes(Integer.valueOf(1));\n        }\n\n        // until\n        if (repeatUntilRadio.isSelected() && repeatFrequencyComboBox.getSelectedIndex() != 0) {\n            Date until = untilDate.getDate();\n\n            if (until == null) {\n                throw new Warning(Resource.getResourceString(\"until_null_error\"));\n            }\n\n            if (until.before(appt.getDate())) {\n                throw new Warning(Resource.getResourceString(\"until_date_error\"));\n            }\n            appt.setRepeatUntil(until);\n            appt.setRepeatFlag(true);\n        } else\n            appt.setRepeatUntil(null);\n\n        // category\n        String cat = (String) categoryBox.getSelectedItem();\n        if (cat.equals(\"\") || cat.equals(CategoryModel.UNCATEGORIZED)) {\n            appt.setCategory(null);\n        } else {\n            appt.setCategory(cat);\n        }\n\n        // reminder times\n        appt.setReminderTimes(new String(custRemTimes));\n\n        appt.setPriority((Integer) prioritySpinner.getValue());\n        return (dateChg);\n    }\n\n    /**\n     * Set the reminder times array based on the data in an Appointment\n     *\n     * @param appt the appointment\n     */\n    private void setCustRemTimes(Appointment appt) {\n        if (appt == null) {\n            // if no appt - set all N\n            custRemTimes = new char[ReminderTimes.getNum()];\n            for (int i = 0; i < ReminderTimes.getNum(); ++i) {\n                custRemTimes[i] = 'N';\n            }\n        } else {\n            try {\n                // set from appt\n                custRemTimes = (appt.getReminderTimes()).toCharArray();\n            } catch (Exception e) {\n                // if null in appt, then set all N\n                for (int i = 0; i < ReminderTimes.getNum(); ++i) {\n                    custRemTimes[i] = 'N';\n                }\n            }\n        }\n    }\n\n    /**\n     * Sets the date.\n     *\n     * @param year  the year\n     * @param month the month\n     * @param day   the day\n     */\n    public void setDate(int year, int month, int day) {\n        year_ = year;\n        month_ = month;\n        day_ = day;\n\n    }\n\n    /**\n     * Sets the start time for a double-clicked new appointment\n     *\n     * @param hour   the appointment start hour\n     * @param minute the appointment start minute\n     */\n    public void setStartTime(int hour, int minute) {\n        starthour = hour;\n        // round double-clicked minute to intervals of 30 for ease of use\n        if (minute - 15 < 0)\n            startminute = 0;\n        else if (minute - 45 < 0)\n            startminute = 30;\n        else {\n            starthour = hour + 1;\n            startminute = 0;\n        }\n    }\n\n    /**\n     * Sets the popup times label to show the user what the popup times are in\n     * human readbale form.\n     */\n    @Override\n    public void setPopupTimesString(String reminderTimes) {\n\n        StringBuffer line1 = new StringBuffer(ReminderTimes.getNum() * 5 + 15);\n        StringBuffer line2 = new StringBuffer(ReminderTimes.getNum() * 5 + 15);\n\n        custRemTimes = reminderTimes.toCharArray();\n\n        // every Y value in custRemTimes is an active reminder time\n        if (custRemTimes != null) {\n            int i = 0;\n\n            // negative times are after the appointment time\n            while (ReminderTimes.getTimes(i) < 0 && i < ReminderTimes.getNum()) {\n                if (custRemTimes[i] == 'Y') {\n                    int abs = -ReminderTimes.getTimes(i);\n                    // string together the times\n                    if (line1.length() > 0) {\n                        line1 = line1.append(\", \").append(abs);\n                    } else {\n                        line1 = line1.append(abs);\n                    }\n                }\n                ++i;\n            }\n            if (line1.length() > 0) {\n                line1 = line1.append(\"   \").append(Resource.getResourceString(\"min_aft_app\"));\n            }\n\n            // positive times are before the appointment\n            while (i < ReminderTimes.getNum()) {\n                if (custRemTimes[i] == 'Y') {\n                    if (line2.length() > 0) {\n                        // string together the times\n                        line2 = line2.append(\", \").append(ReminderTimes.getTimes(i));\n                    } else {\n                        line2 = line2.append(ReminderTimes.getTimes(i));\n                    }\n                }\n                ++i;\n            }\n            if (line2.length() > 0) {\n                line2 = line2.append(\"   \").append(Resource.getResourceString(\"min_bef_app\"));\n            }\n\n        }\n\n        // set the label, which is html for formatting\n        popupTimesLabel.setText(\"<html><p align=RIGHT>\" + line1 + \"<br>\" + line2);\n    }\n\n    /**\n     * set the UI components to show the data in an Appointment\n     *\n     * @param key           the appointment key or -1 for a new appt\n     * @param defaultApptIn the default appt - only passed in if we are copying an\n     *                      existing appt\n     */\n    public void showapp(int key, Appointment defaultApptIn) {\n        Appointment defaultAppt = defaultApptIn;\n        currentlyShownAppointmentKey = key;\n\n        // default to untimed - will change later if timed\n        startTimePanel.setEnabled(false);\n        endTimePanel.setEnabled(false);\n\n        untimedCheckBox.setSelected(true);\n        dateChangeCheckBox.setSelected(false);\n        encryptBox.setSelected(false);\n        appointmentBodyTextArea.setEditable(true);\n        apptTitleField.setEditable(true);\n        decryptButton.setEnabled(false);\n\n        saveButton.setEnabled(true);\n        saveCloseButton.setEnabled(true);\n\n        // default to unset for toggles\n        for (JToggleButton tog : dayToggles) {\n            tog.setSelected(false);\n        }\n\n        // get default appt values from XML, if any\n        if (defaultAppt == null) {\n            defaultAppt = AppointmentModel.getDefaultAppointment();\n        }\n        // a key of -1 means to show a new blank appointment\n        if (currentlyShownAppointmentKey == -1 && defaultAppt == null) {\n\n            // case for double-clicked new appt in editor with preset time\n            if (starthour > 0) {\n                // set start and end time to time passed from double-click\n                GregorianCalendar cal = new GregorianCalendar();\n                cal.set(Calendar.HOUR_OF_DAY, starthour);\n                cal.set(Calendar.MINUTE, startminute);\n                cal.set(Calendar.SECOND, 0);\n                cal.set(Calendar.MILLISECOND, 0);\n                startTimePanel.setTime(cal.getTime());\n                // set default duration to 1 hour\n                cal.set(Calendar.HOUR_OF_DAY, starthour + 1);\n                endTimePanel.setTime(cal.getTime());\n\n                // enable time widgets\n                startTimePanel.setEnabled(true);\n                endTimePanel.setEnabled(true);\n                untimedCheckBox.setSelected(false);\n            } else {\n                startTimePanel.setTime(DateUtil.setToMidnight(new Date()));\n                endTimePanel.setTime(DateUtil.setToMidnight(new Date()));\n            }\n\n            // initialize new appt values\n            categoryBox.setSelectedIndex(0);\n            apptTitleField.setText(\"\");\n            todoCheckBox.setSelected(false); // todo unchecked\n            colorComboBox.setSelectedIndex(3); // color = black\n            vacationCheckBox.setSelected(false); // vacation unchecked\n            halfDayVacationCheckBox.setSelected(false); // half-day unchecked\n            holidayCheckBox.setSelected(false); // holiday unchecked\n            privateCheckBox.setSelected(false); // private unchecked\n            appointmentBodyTextArea.setText(\"\"); // clear appt text\n            repeatFrequencyComboBox.setSelectedIndex(0); // freq = once\n            numberOfRepeatsSpinner.setEnabled(true);\n            numberOfRepeatsSpinner.setValue(Integer.valueOf(1)); // times = 1\n            repeatForeverRadio.setSelected(false);\n            repeatUntilRadio.setSelected(false);\n            untilDate.setEnabled(false);\n            showRepeatNumberCheckBox.setSelected(false);\n            showRepeatNumberCheckBox.setEnabled(false);\n            ResourceHelper.setText(newAppointmentIndicatorLabel, \"*****_NEW_APPT_*****\");\n\n            dateChangeCheckBox.setEnabled(false);\n            newdatefield.setEnabled(false);\n\n            setCustRemTimes(null);\n            setPopupTimesString(new String(custRemTimes));\n\n            linkPanel.setOwner(null);\n            prioritySpinner.setValue(DEFAULT_PRIORITY);\n\n        } else {\n\n            try {\n\n                Appointment appt = null;\n                if (currentlyShownAppointmentKey == -1) {\n                    // new appt - but load from default appt\n                    ResourceHelper.setText(newAppointmentIndicatorLabel, \"*****_NEW_APPT_*****\");\n                    appt = defaultAppt;\n                    linkPanel.setOwner(null);\n                } else {\n                    // get the appt Appointment from the calmodel\n                    newAppointmentIndicatorLabel.setText(\"    \");\n                    appt = AppointmentModel.getReference().getAppt(currentlyShownAppointmentKey);\n                    linkPanel.setOwner(appt);\n                }\n\n                // duration\n                Integer duration = appt.getDuration();\n                int dur = 0;\n                if (duration != null)\n                    dur = duration.intValue();\n\n                // if preset time - don't take time from default appt\n                if (currentlyShownAppointmentKey == -1 && starthour > 0) {\n                    // set start and end time to time passed from double-click\n                    GregorianCalendar cal = new GregorianCalendar();\n                    cal.set(Calendar.HOUR_OF_DAY, starthour);\n                    cal.set(Calendar.MINUTE, startminute);\n                    cal.set(Calendar.SECOND, 0);\n                    cal.set(Calendar.MILLISECOND, 0);\n                    startTimePanel.setTime(cal.getTime());\n                    // set default duration to 1 hour\n                    cal.set(Calendar.HOUR_OF_DAY, starthour + 1);\n                    endTimePanel.setTime(cal.getTime());\n\n                    // enable time widgets\n                    startTimePanel.setEnabled(true);\n                    endTimePanel.setEnabled(true);\n                    untimedCheckBox.setSelected(false);\n                } else {\n                    // set hour and minute widgets\n                    startTimePanel.setTime(appt.getDate());\n\n                    Date end = new Date();\n                    end.setTime(appt.getDate().getTime() + (dur * 60 * 1000));\n                    endTimePanel.setTime(end);\n\n                    // check if we just have a \"note\" (non-timed appt)\n                    boolean untimed = AppointmentModel.isNote(appt);\n                    if (!untimed) {\n                        // enable time widgets\n                        startTimePanel.setEnabled(true);\n                        endTimePanel.setEnabled(true);\n                        untimedCheckBox.setSelected(false);\n                    }\n\n                }\n\n                // set ToDo checkbox\n                todoCheckBox.setSelected(appt.isTodo());\n\n                // set vacation checkbox\n                vacationCheckBox.setSelected(false);\n                Integer ii = appt.getVacation();\n                if (ii != null && ii.intValue() == 1)\n                    vacationCheckBox.setSelected(true);\n\n                // set half-day checkbox\n                halfDayVacationCheckBox.setSelected(false);\n                if (ii != null && ii.intValue() == 2)\n                    halfDayVacationCheckBox.setSelected(true);\n\n                // holiday checkbox\n                holidayCheckBox.setSelected(false);\n                ii = appt.getHoliday();\n                if (ii != null && ii.intValue() == 1)\n                    holidayCheckBox.setSelected(true);\n\n                // private checkbox\n                privateCheckBox.setSelected(appt.isPrivate());\n\n                if (appt.isEncrypted()) {\n                    apptTitleField.setText(Resource.getResourceString(\"EncryptedItem\"));\n                    apptTitleField.setEditable(false);\n                    appointmentBodyTextArea.setText(\"\");\n                    appointmentBodyTextArea.setEditable(false);\n                    decryptButton.setEnabled(true);\n                } else {                  \n                    appointmentBodyTextArea.setText(appt.getBody());\n                    apptTitleField.setText(appt.getTitle());\n                }\n\n                // color\n                colorComboBox.setActive(false);\n                String sel = appt.getColor();\n                if (sel != null) {\n\n                    if (sel.equals(\"red\") || sel.equals(COLOR1)) {\n                        colorComboBox.setSelectedIndex(0);\n                    } else if (sel.equals(\"blue\")|| sel.equals(COLOR2)) {\n                        colorComboBox.setSelectedIndex(1);\n                    } else if (sel.equals(\"green\")|| sel.equals(COLOR3)) {\n                        colorComboBox.setSelectedIndex(2);\n                    } else if (sel.equals(\"black\")|| sel.equals(COLOR4)) {\n                        colorComboBox.setSelectedIndex(3);\n                    } else if (sel.equals(\"white\")|| sel.equals(COLOR5)) {\n                        colorComboBox.setSelectedIndex(4);\n                    } else {\n                        // if int, then update the color chooser, otherwise,\n                        // default to strike\n                        try {\n                            Integer i = Integer.parseInt(sel);\n                            colorComboBox.setChosenColor(new Color(i));\n                            colorComboBox.setSelectedIndex(1);\n                            colorComboBox.setSelectedIndex(6);\n                        } catch (Exception e) {\n                            colorComboBox.setSelectedIndex(5);\n                        }\n                    }\n\n                } else {\n                    // default is black\n                    colorComboBox.setSelectedIndex(3);\n                }\n                colorComboBox.setActive(true);\n\n                dateChangeCheckBox.setEnabled(true);\n                newdatefield.setEnabled(false);\n\n                // repeat frequency - turn on/off widgets as needed\n                String rpt = Repeat.getFreq(appt.getFrequency());\n                if (rpt != null && (rpt.equals(Repeat.NDAYS) || rpt.equals(Repeat.NWEEKS) || rpt.equals(Repeat.NMONTHS)\n                        || rpt.equals(Repeat.NYEARS))) {\n                    nTimesValue.setValue(Integer.valueOf(Repeat.getNValue(appt.getFrequency())));\n                }\n\n                if (rpt != null && rpt.equals(Repeat.DAYLIST)) {\n                    Collection<Integer> daylist = Repeat.getDaylist(appt.getFrequency());\n                    if (daylist != null) {\n                        if (daylist.contains(Integer.valueOf(Calendar.SUNDAY)))\n                            dayToggles[0].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.MONDAY)))\n                            dayToggles[1].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.TUESDAY)))\n                            dayToggles[2].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.WEDNESDAY)))\n                            dayToggles[3].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.THURSDAY)))\n                            dayToggles[4].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.FRIDAY)))\n                            dayToggles[5].setSelected(true);\n                        if (daylist.contains(Integer.valueOf(Calendar.SATURDAY)))\n                            dayToggles[6].setSelected(true);\n                    }\n                }\n\n                showRepeatNumberCheckBox.setSelected(Repeat.getRptNum(appt.getFrequency()));\n\n                repeatFrequencyComboBox.setSelectedItem(Repeat.getFreqString(rpt));\n\n                // repeat times\n                Integer tm = appt.getTimes();\n                numberOfRepeatsSpinner.setValue(Integer.valueOf(1));\n                untilDate.setDate(null);\n\n                // if until date is set, then that takes priority\n                if (appt.getRepeatUntil() != null) {\n                    untilDate.setDate(appt.getRepeatUntil());\n                    repeatUntilRadio.setSelected(true);\n                } else if (tm != null) {\n                    if (tm.intValue() == Repeat.MAGIC_RPT_FOREVER_VALUE) {\n                        repeatForeverRadio.setSelected(true);\n                    } else {\n                        numberOfRepeatsSpinner.setValue(tm == 0 ? 1 : tm);\n                        repeatTimesRadio.setSelected(true);\n                    }\n                } else {\n                    repeatTimesRadio.setSelected(true);\n                }\n\n                // set category combo box\n                String cat = appt.getCategory();\n                if (cat != null && !cat.equals(\"\")) {\n                    categoryBox.setSelectedItem(cat);\n                } else {\n                    categoryBox.setSelectedIndex(0);\n                }\n\n                // set reminder times\n                setCustRemTimes(appt);\n                setPopupTimesString(new String(custRemTimes));\n\n                // encryption\n                encryptBox.setSelected(appt.isEncrypted());\n                if (appt.isEncrypted()) {\n                    saveButton.setEnabled(false);\n                    saveCloseButton.setEnabled(false);\n                }\n\n                // set priority\n                Integer p = Integer.valueOf(DEFAULT_PRIORITY);\n                Integer priority = appt.getPriority();\n                if (priority != null)\n                    p = priority;\n                prioritySpinner.setValue(p);\n\n            } catch (Exception e) {\n                Errmsg.getErrorHandler().errmsg(e);\n                Exception ne = new Exception(Resource.getResourceString(\"appt_error\"));\n                Errmsg.getErrorHandler().errmsg(ne);\n\n            }\n        }\n\n        // set components based on repeat frequency\n        timesEnable();\n\n        apptTitleField.requestFocus();\n\n    }\n\n    /**\n     * enable the proper repeat widgets based on repeat frequency\n     */\n    private void timesEnable() {\n        if (repeatFrequencyComboBox.getSelectedIndex() == 0) {\n            numberOfRepeatsSpinner.setEnabled(false);\n            repeatForeverRadio.setEnabled(false);\n            repeatTimesRadio.setEnabled(false);\n            repeatUntilRadio.setEnabled(false);\n            showRepeatNumberCheckBox.setEnabled(false);\n            untilDate.setEnabled(false);\n\n        } else {\n            numberOfRepeatsSpinner.setEnabled(true);\n            repeatForeverRadio.setEnabled(true);\n            repeatTimesRadio.setEnabled(true);\n            repeatUntilRadio.setEnabled(true);\n            showRepeatNumberCheckBox.setEnabled(true);\n            untilDate.setEnabled(true);\n        }\n\n        String english = Repeat.freqToEnglish((String) repeatFrequencyComboBox.getSelectedItem());\n        if (english.equals(Repeat.NDAYS) || english.equals(Repeat.NWEEKS) || english.equals(Repeat.NMONTHS)\n                || english.equals(Repeat.NYEARS)) {\n            nTimesValue.setVisible(true);\n            selectDayButtonPanel.setVisible(false);\n        } else if (english.equals(Repeat.DAYLIST)) {\n            selectDayButtonPanel.setVisible(true);\n            nTimesValue.setVisible(false);\n        } else {\n            selectDayButtonPanel.setVisible(false);\n            nTimesValue.setVisible(false);\n        }\n    }\n\n    @Override\n    public void update(ChangeEvent event) {\n        if (event.getModel() instanceof CategoryModel) {\n            reloadCategories();\n        }\n\n    }\n\n    public void cleanup() {\n        CategoryModel.getReference().removeListener(this);\n        if (linkPanel != null)\n            linkPanel.cleanup();\n    }\n\n    private void reloadCategories() {\n\n        String cat = (String) categoryBox.getSelectedItem();\n        categoryBox.removeAllItems();\n        try {\n            Collection<String> cats = CategoryModel.getReference().getCategories();\n            Iterator<String> it = cats.iterator();\n            while (it.hasNext()) {\n                categoryBox.addItem(it.next());\n            }\n\n            if (cat != null && !cat.equals(\"\")) {\n                categoryBox.setSelectedItem(cat);\n            } else {\n                categoryBox.setSelectedIndex(0);\n            }\n        } catch (Exception e) {\n            Errmsg.getErrorHandler().errmsg(e);\n        }\n    }\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/AppointmentTextFormat.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2009 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.entity.Appointment;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * common code to format an appointment's text for display on the UI\n * \n */\npublic class AppointmentTextFormat {\n\n\t/**\n\t * generate the appointment string for display of an appointment on a given\n\t * day. Optionally prepend things like time of day, repeat number, vacation\n\t * day number...\n\t * \n\t * @param appt\n\t *            - the appointment\n\t * @param date\n\t *            - the date that the appt is being displayed for or null. The\n\t *            date is used to identify an instance of a repeating appt\n\t * @return the formatted appt string to display\n\t */\n\tstatic public String format(Appointment appt, Date date, boolean force_full_text) {\n\n\t\tif (appt.getText() == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\t\n\t\tCalendar day = new GregorianCalendar();\n\t\tday.setTime(date);\n\n\t\tString theFormattedText = \"\";\n\n\t\t// add time in front of the appt text\n\t\tif (!AppointmentModel.isNote(appt)) {\n\t\t\tDate d = appt.getDate();\n\t\t\tif( d != null )\n\t\t\t{\t\n\t\t\t\tSimpleDateFormat sdf = AppointmentModel.getTimeFormat();\n\t\t\t\ttheFormattedText += sdf.format(d) + \" \";\n\t\t\t}\n\t\t}\n\n\t\tif (appt.isEncrypted()) {\n\t\t\t// don't show the raw encrypted text to the user - show a pre-defined short string\n\t\t\ttheFormattedText += Resource\n\t\t\t\t\t.getResourceString(\"EncryptedItemShort\");\n\t\t} else {\n\t\t\t// if the text is empty - skip it - should never be\n\t\t\tString xx = appt.getText();\n\t\t\tString trunc = Prefs.getPref(PrefName.TRUNCAPPT);\n\t\t\tif (!force_full_text && trunc.equals(\"true\")) {\n\t\t\t\t// !!!!! only show first line of appointment text !!!!!!\n\t\t\t\tint ii = xx.indexOf('\\n');\n\t\t\t\tif (ii != -1) {\n\t\t\t\t\ttheFormattedText += xx.substring(0, ii);\n\t\t\t\t} else {\n\t\t\t\t\ttheFormattedText += xx;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttheFormattedText += xx;\n\t\t\t}\n\t\t}\n\n\t\t// add repeat number\n\t\tif (Repeat.getRptNum(appt.getFrequency())) {\n\t\t\ttheFormattedText += \" (\" + Repeat.calculateRepeatNumber(day, appt)\n\t\t\t\t\t+ \")\";\n\t\t}\n\n\t\tif (appt.getVacation() != null && appt.getVacation().intValue() != 0) {\n\t\t\tday.set(Calendar.HOUR_OF_DAY, 11);\n\t\t\tdouble vacationCount = AppointmentModel.getReference()\n\t\t\t\t\t.vacationCount(day.getTime());\n\t\t\ttheFormattedText = \"[\" + vacationCount + \"] \" + theFormattedText;\n\t\t}\n\n\t\treturn theFormattedText;\n\t}\n\t\n\tstatic public String format(Appointment appt, Date date) {\n\t\treturn format(appt,date,false);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/ApptBox.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.GradientPaint;\nimport java.awt.Graphics2D;\nimport java.awt.Paint;\nimport java.awt.Rectangle;\nimport java.awt.Shape;\nimport java.awt.event.ActionListener;\nimport java.awt.font.FontRenderContext;\nimport java.awt.font.LineBreakMeasurer;\nimport java.awt.font.TextAttribute;\nimport java.awt.font.TextLayout;\nimport java.awt.geom.AffineTransform;\nimport java.awt.geom.Rectangle2D;\nimport java.awt.image.AffineTransformOp;\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\nimport java.text.AttributedCharacterIterator;\nimport java.text.AttributedString;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPopupMenu;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.LinkModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.Link;\nimport net.sf.borg.ui.ClipBoard;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * ApptBox is used to draw timed appointments on the time-grid part of the day\n * and week UIs.\n */\nclass ApptBox extends Box implements Box.Draggable {\n\t\n\tfinal static private int inset = 2;\n\n\t// rounded rectangle radius\n\tfinal static private int radius = 2;\n\n\tprivate final SimpleDateFormat sdf = new SimpleDateFormat(\"hh:mm\");\n\tprivate final SimpleDateFormat sdf24 = new SimpleDateFormat(\"HH:mm\");\n\n\n\t/**\n\t * Layout all boxes for a particular day by determining how many overlap and\n\t * then setting the horizontal position and width to fit them all together\n\t * in the day grid.\n\t * \n\t * @param boxlist the list of all ApptBox objects for a day\n\t * @param starthr first hour shown on the grid\n\t * @param endhr last hour shown on the grid\n\t */\n\tstatic public void layoutBoxes(List<ApptBox> boxlist, int starthr, int endhr) {\n\n\t\t// convert to minutes\n\t\tdouble startmin = starthr * 60;\n\t\tdouble endmin = endhr * 60;\n\n\t\t// initialize the boxes\n\t\tfor (ApptBox box : boxlist) {\n\n\t\t\tAppointment ap = box.appt;\n\t\t\tDate d = ap.getDate();\n\n\t\t\t// shouldn't ever happen - but if appt has no date\n\t\t\t// we can't show it\n\t\t\tif (d == null)\n\t\t\t\tcontinue;\n\n\t\t\t// determine appt start and end minutes\n\t\t\tGregorianCalendar acal = new GregorianCalendar();\n\t\t\tacal.setTime(d);\n\t\t\tdouble apstartmin = 60 * acal.get(Calendar.HOUR_OF_DAY)\n\t\t\t\t\t+ acal.get(Calendar.MINUTE);\n\t\t\tint dur = 0;\n\t\t\tInteger duri = ap.getDuration();\n\t\t\tif (duri != null) {\n\t\t\t\tdur = duri.intValue();\n\t\t\t}\n\t\t\tdouble apendmin = apstartmin + dur;\n\n\t\t\t// if appointment starts before shown hours or ends after shown\n\t\t\t// hours\n\t\t\t// adjust it to draw up to the grid boundary\n\t\t\tif (apstartmin < startmin)\n\t\t\t\tapstartmin = startmin;\n\t\t\tif (apendmin > endmin)\n\t\t\t\tapendmin = endmin;\n\n\t\t\t// fraction of the grid that the top of the box is below the grid\n\t\t\t// top\n\t\t\t// lots of items are calculated as fractions of the grid size so\n\t\t\t// that\n\t\t\t// they stay the same if the grid size changes\n\t\t\tbox.setTopAdjustment((apstartmin - startmin) / (endmin - startmin));\n\n\t\t\t// adjust the bottom ever so slightly that appts that touch top\n\t\t\t// to bottom do not get detected as overlapping when rounding errors\n\t\t\t// creep in\n\t\t\t// in\n\t\t\tbox\n\t\t\t\t\t.setBottomAdjustment(((apendmin - startmin) / (endmin - startmin)) - 0.000001);\n\n\t\t}\n\n\t\t// determine how many appointments each appointment overlaps with\n\t\t// to do this, check how many appointments occur for every 5 minute\n\t\t// interval on the time grid\n\t\tfor (int t = (int) startmin; t <= (int) endmin; t += 5) {\n\n\t\t\t// make a list of appointments that exist at time t\n\t\t\tArrayList<ApptBox> lst = new ArrayList<ApptBox>();\n\t\t\tfor (ApptBox curBox : boxlist) {\n\t\t\t\tCalendar cal = new GregorianCalendar();\n\t\t\t\tcal.setTime(curBox.appt.getDate());\n\t\t\t\tint amin = cal.get(Calendar.HOUR_OF_DAY) * 60\n\t\t\t\t\t\t+ cal.get(Calendar.MINUTE);\n\t\t\t\tif (amin <= t\n\t\t\t\t\t\t&& (amin + curBox.appt.getDuration().intValue()) > t)\n\t\t\t\t\tlst.add(curBox);\n\t\t\t}\n\n\t\t\t// for all appts that exist at time t, adjust their max\n\t\t\t// number of overlaps if the value for time t is bigger than\n\t\t\t// their current value\n\t\t\tfor (ApptBox curBox : lst) {\n\t\t\t\tcurBox.setMaxAcrossAtOneTime(Math.max(curBox\n\t\t\t\t\t\t.getMaxAcrossAtOneTime(), lst.size()));\n\t\t\t}\n\t\t}\n\n\t\t// sort the list of boxes by how many overlaps a box has. The ones with\n\t\t// the most\n\t\t// need to be placed on the grid first.\n\t\tCollections.sort(boxlist, new Comparator<ApptBox>() {\n\t\t\t@Override\n\t\t\tpublic int compare(ApptBox obj1, ApptBox obj2) {\n\t\t\t\tint diff = obj2.getMaxAcrossAtOneTime()\n\t\t\t\t\t\t- obj1.getMaxAcrossAtOneTime();\n\t\t\t\tif (diff != 0)\n\t\t\t\t\treturn (diff);\n\t\t\t\treturn (int) (obj2.getDate().getTime() - obj1.getDate()\n\t\t\t\t\t\t.getTime());\n\t\t\t}\n\t\t});\n\n\t\t// determine left and right for each box by placing boxes on the grid\n\t\t// one at a time\n\t\tfor (ApptBox curBox : boxlist) {\n\n\t\t\t// curBox is the box we are trying to place in the grid\n\n\t\t\t// farthest right edge of any placed appts that overlap the current\n\t\t\tdouble maxRightOfPlaced = 0;\n\n\t\t\tfor (ApptBox otherBox : boxlist) {\n\t\t\t\tif (otherBox == curBox)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// detect overlap\n\t\t\t\tif (otherBox.getTopAdjustment() > curBox.getBottomAdjustment()\n\t\t\t\t\t\t|| otherBox.getBottomAdjustment() < curBox\n\t\t\t\t\t\t\t\t.getTopAdjustment()) {\n\t\t\t\t\t// no overlap\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// make every box that overlaps the same size as the others it\n\t\t\t\t// overlaps with\n\t\t\t\t// even if it's not involved in the spot where the max number of\n\t\t\t\t// appts overlap\n\t\t\t\tif (otherBox.getMaxAcrossAtOneTime() < curBox\n\t\t\t\t\t\t.getMaxAcrossAtOneTime())\n\t\t\t\t\totherBox.setMaxAcrossAtOneTime(curBox\n\t\t\t\t\t\t\t.getMaxAcrossAtOneTime());\n\n\t\t\t\t// if the other box has been place (it's horizontal position\n\t\t\t\t// determined),\n\t\t\t\t// then make sure that the current box is to the right of it\n\t\t\t\tif (otherBox.isPlaced()\n\t\t\t\t\t\t&& otherBox.getRightAdjustment() > maxRightOfPlaced) {\n\t\t\t\t\tmaxRightOfPlaced = otherBox.getRightAdjustment();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check if due to the order that boxes were placed and what\n\t\t\t// overlaps,\n\t\t\t// there is no more room across. if so, our prior overlap\n\t\t\t// calculations\n\t\t\t// should have ensured that there is an empty slot to place the box\n\t\t\t// in\n\t\t\t// note: any 3 place decimales are to guard against rounding errors\n\t\t\t// - which did happen\n\t\t\tif (maxRightOfPlaced >= 0.999) {\n\n\t\t\t\t// need to fine a \"hole\" in the grid, so check each horizontal\n\t\t\t\t// spot\n\t\t\t\t// looking for an empty one\n\t\t\t\tfor (int slot = 0; slot < curBox.getMaxAcrossAtOneTime(); slot++) {\n\n\t\t\t\t\t// check if appt is in this slot\n\t\t\t\t\tboolean slotTaken = false;\n\t\t\t\t\tfor (ApptBox otherBox : boxlist) {\n\t\t\t\t\t\tif (otherBox == curBox)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tif (!otherBox.isPlaced())\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tif (otherBox.getTopAdjustment() > curBox\n\t\t\t\t\t\t\t\t.getBottomAdjustment()\n\t\t\t\t\t\t\t\t|| otherBox.getBottomAdjustment() < curBox\n\t\t\t\t\t\t\t\t\t\t.getTopAdjustment()) {\n\t\t\t\t\t\t\t// no overlap\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// determine if the appt is in this slot\n\t\t\t\t\t\tif (Math.abs(otherBox.getLeftAdjustment()\n\t\t\t\t\t\t\t\t- ((double) slot)\n\t\t\t\t\t\t\t\t/ (double) otherBox.getMaxAcrossAtOneTime()) < 0.001) {\n\t\t\t\t\t\t\t// yes, otherBox is in the slot, we can't place the\n\t\t\t\t\t\t\t// current box there\n\t\t\t\t\t\t\tslotTaken = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!slotTaken) {\n\n\t\t\t\t\t\t\t// empty slot found, place the box there\n\t\t\t\t\t\t\tcurBox.setLeftAdjustment((double) slot\n\t\t\t\t\t\t\t\t\t/ (double) curBox.getMaxAcrossAtOneTime());\n\t\t\t\t\t\t\tcurBox.setRightAdjustment(curBox\n\t\t\t\t\t\t\t\t\t.getLeftAdjustment()\n\t\t\t\t\t\t\t\t\t+ (1 / (double) curBox\n\t\t\t\t\t\t\t\t\t\t\t.getMaxAcrossAtOneTime()));\n\t\t\t\t\t\t\tcurBox.setPlaced(true);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (curBox.isPlaced()) // if we were placed already in the\n\t\t\t\t// out-of-slot code above\n\t\t\t\tcontinue;\n\n\t\t\t// place the box horizontally\n\t\t\t// left side is to the right of the others already placed\n\t\t\tcurBox.setLeftAdjustment(maxRightOfPlaced);\n\t\t\t// right side is left + width\n\t\t\tcurBox.setRightAdjustment(curBox.getLeftAdjustment()\n\t\t\t\t\t+ (1 / (double) curBox.getMaxAcrossAtOneTime()));\n\t\t\tcurBox.setPlaced(true);\n\t\t}\n\n\t\t// calculate the bounds of each box by multiplying the fractional\n\t\t// adjustments\n\t\t// with real coordinates of the grid box for the day\n\t\tfor (ApptBox b : boxlist) {\n\t\t\tRectangle r = new Rectangle();\n\n\t\t\tr.x = (int) (b.bounds.x + b.bounds.width * b.getLeftAdjustment());\n\t\t\tr.y = (int) (b.bounds.y + b.bounds.height * b.getTopAdjustment());\n\t\t\tr.height = (int) ((b.getBottomAdjustment() - b.getTopAdjustment()) * b.bounds.height);\n\t\t\tr.width = (int) ((b.getRightAdjustment() - b.getLeftAdjustment()) * b.bounds.width);\n\t\t\tb.setBounds(r);\n\t\t}\n\t}\n\n\t// the appointment associated with this box\n\tprivate Appointment appt = null;\n\n\t// fraction of the available grid height at which the bottom should be drawn\n\tprivate double bottom; \n\n\tprivate final Date date; // date being displayed - not necessarily date of the appt\n\t\n\t// flag to indicate if the appt has links\n\tprivate boolean hasLink = false;\n\n\tprivate boolean isPlaced = false; // whether or not this box has been placed (position calculated by the layout code)\n\n\tprivate double left; // fraction of the available grid width at which the left side is drawn\n\n\tprivate int maxAcrossAtOneTime = 0; // max number of appts overlapping any others that overlap this one\n\t\n\tprivate final String displayText;\n\tprivate final String tooltipText;\n\n\tprivate int oldFontHeight = -1; // used for sizing the todo marker image\n\n\tprivate JPopupMenu popmenu = null; // popup menu\n\n\tprivate double right; // fraction of the available grid width at which the right side should be drawn\n\n\tprivate Icon todoIcon = null; // todo icon\n\tprivate Icon lockIcon = null; // private icon\n\n\tprivate String todoMarker = null; // textual todo marker\n\n\tprivate double top; // fraction of the available grid height at which the top should be drawn\n\n\t/**\n\t * constructor.\n\t * \n\t * @param d date that the box is in - not always the appt date\n\t * @param ap the appointment represented by this box\n\t * @param bounds box bounds\n\t * @param clip box clip\n\t */\n\tpublic ApptBox(Date d, Appointment ap, Rectangle bounds, Rectangle clip) {\n\n\t\tsuper(bounds, clip);\n\n\t\tappt = ap;\n\t\tdate = d;\n\n\t\t// determine the todo marker - image or textual - based on prefs\n\t\tString iconname = Prefs.getPref(PrefName.UCS_MARKER);\n\t\tString use_marker = Prefs.getPref(PrefName.UCS_MARKTODO);\n\t\tif (use_marker.equals(\"true\")) {\n\t\t\tif (iconname.endsWith(\".gif\") || iconname.endsWith(\".jpg\")) {\n\t\t\t\ttodoIcon = IconHelper.getIcon(\n\t\t\t\t\t\t\"/resource/\" + iconname);\n\t\t\t} else {\n\t\t\t\ttodoMarker = iconname;\n\t\t\t}\n\t\t}\n\t\t\n\t\tlockIcon = IconHelper.getIcon(\n\t\t\t\t\"/resource/\" + \"lock16.png\");\n\n\t\t// determine links flag\n\t\tCollection<Link> atts;\n\t\ttry {\n\t\t\tatts = LinkModel.getReference().getLinks(appt);\n\t\t\tif (atts != null && atts.size() > 0)\n\t\t\t\thasLink = true;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\t// format the appt text\n\t\tdisplayText = AppointmentTextFormat.format(ap, d);\n\t\ttooltipText = AppointmentTextFormat.format(ap, d, true);\n\n\t}\n\n\t/**\n\t * react to a delete request on the box.\n\t */\n\t@Override\n\tpublic void delete() {\n\t\tAppointmentModel.getReference().delAppt(appt.getKey());\n\t}\n\n\t/**\n\t * draw the box.\n\t * \n\t * @param g2 the graphics to draw in\n\t * @param comp the component that the graphics is in\n\t */\n\t@Override\n\tpublic void draw(Graphics2D g2, Component comp) {\n\t\tShape s = g2.getClip();\n\t\tif (clip != null)\n\t\t\tg2.setClip(clip);\n\n\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\t\t\n\n\t\tColor textColor = Color.BLACK;\n\t\tTheme t = Theme.getCurrentTheme();\n\t\ttextColor = new Color(t.colorFromString(getTextColor()));\n\n\t\t// resize todoIcon if needed to match the text size\n\t\tif (oldFontHeight != smfontHeight) {\n\t\t\tif (todoIcon != null) {\n\t\t\t\ttry {\n\t\t\t\t\t// get todo marker image\n\t\t\t\t\tBufferedImage image1 = ImageIO.read(getClass().getResource(\n\t\t\t\t\t\t\t\"/resource/\" + Prefs.getPref(PrefName.UCS_MARKER)));\n\t\t\t\t\tdouble size = image1.getHeight();\n\n\t\t\t\t\t// scale to 1/2 font height\n\t\t\t\t\tdouble scale = smfontHeight / (2 * size);\n\t\t\t\t\tAffineTransform tx = AffineTransform.getScaleInstance(\n\t\t\t\t\t\t\tscale, scale);\n\t\t\t\t\tAffineTransformOp op = new AffineTransformOp(tx,\n\t\t\t\t\t\t\tAffineTransformOp.TYPE_BICUBIC);\n\t\t\t\t\tBufferedImage rImage = op.filter(image1, null);\n\t\t\t\t\ttodoIcon = new ImageIcon(rImage);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// get todo marker image\n\t\t\t\tBufferedImage image1 = ImageIO.read(getClass().getResource(\n\t\t\t\t\t\t\"/resource/\" + \"lock16.png\"));\n\t\t\t\tdouble size = image1.getHeight();\n\n\t\t\t\t// scale to 1/2 font height\n\t\t\t\tdouble scale = smfontHeight / (2 * size);\n\t\t\t\tAffineTransform tx = AffineTransform.getScaleInstance(\n\t\t\t\t\t\tscale, scale);\n\t\t\t\tAffineTransformOp op = new AffineTransformOp(tx,\n\t\t\t\t\t\tAffineTransformOp.TYPE_BICUBIC);\n\t\t\t\tBufferedImage rImage = op.filter(image1, null);\n\t\t\t\tlockIcon = new ImageIcon(rImage);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\toldFontHeight = smfontHeight;\n\t\t}\n\n\t\t// draw the box\n\t\tPaint paint = g2.getPaint();\n\t\tif( Prefs.getBoolPref(PrefName.GRADIENT_APPTS)){\n\t\t\tGradientPaint gp = new GradientPaint(bounds.x, bounds.y + inset + smfontHeight, getBoxColor(),\n\t\t\t\t\tbounds.x, bounds.y + 2*bounds.height, textColor);\n\t\t\tg2.setPaint(gp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tg2.setColor(getBoxColor());\n\t\t}\n\t\tg2.fillRoundRect(bounds.x + inset, bounds.y\n\t\t\t\t+ inset, bounds.width - inset,\n\t\t\t\tbounds.height - inset, radius * radius, radius\n\t\t\t\t* radius);\n\t\t\n\t\tg2.setPaint(paint);\n\t\t// add a border around the box\n\t\tg2.setColor(textColor);\n\t\tif (isSelected) {\n\t\t\t// set the border to a different color if it is selected\n\t\t\tg2.setColor(Color.CYAN);\n\t\t}\n\t\t\n\t\t// draw the border\n\t\tg2.drawRoundRect(bounds.x + inset, bounds.y\n\t\t\t\t+ inset, bounds.width - inset,\n\t\t\t\tbounds.height - inset, radius * radius, radius\n\t\t\t\t\t\t* radius);\n\t\t\n\n\t\t// set the clip for the appt text\n\t\tg2.clipRect(bounds.x, bounds.y, bounds.width, bounds.height);\n\n\t\tg2.setColor(textColor);\n\t\t\n\t\t// appt text\n\t\tString text = getText();\n\t\t\n\t\t// prepend link marker\n\t\tif (hasLink) {\n\t\t\ttext = \"@ \" + text;\n\t\t}\n\t\t\n\t\tint offset = 0;\n\n\t\t// prepend todo marker and draw the string\n\t\tif (isTodo() && todoIcon != null) {\n\t\t\toffset = todoIcon.getIconWidth();\n\t\t\ttodoIcon.paintIcon(comp, g2, bounds.x + radius, bounds.y + radius\n\t\t\t\t\t+ smfontHeight / 2);\n\t\t\t\n\t\t} else if (isTodo() && todoMarker != null) {\n\t\t\ttext = todoMarker + \" \" + text;\n\t\t}\n\t\t\n\t\tif( appt.isPrivate())\n\t\t{\n\t\t\tlockIcon.paintIcon(comp, g2, bounds.x + radius + offset, bounds.y + radius\n\t\t\t\t\t+ smfontHeight / 2);\n\t\t\toffset += lockIcon.getIconWidth();\n\t\t}\n\t\t\n\t\tdrawWrappedString(g2, text, bounds.x + radius\n\t\t\t\t+ offset, bounds.y + radius, bounds.width\n\t\t\t\t- radius, getTextColor().equals(\"strike\"));\n\t\t\n\t\tif( isSelected)\n\t\t{\n\t\t\tg2.setColor(new Color(50, 50, 50));\n\t\t\tRectangle2D bb = g2.getFont().getStringBounds(\"00:00\",\n\t\t\t\t\tg2.getFontRenderContext());\n\t\t\t\n\t\t\tg2.fillRect(bounds.x + 2, bounds.y\n\t\t\t\t\t+ bounds.height - (int) bb.getHeight(),\n\t\t\t\t\t(int) bb.getWidth(), (int) bb.getHeight());\n\t\t\tg2.setColor(Color.WHITE);\n\t\t\t\n\t\t\tDate endtime = new Date(appt.getDate().getTime() + 1000*60*appt.getDuration().intValue());\n\t\t\tif( Prefs.getBoolPref(PrefName.MILTIME))\n\t\t\t\tg2.drawString(sdf24.format(endtime), bounds.x + 2,\n\t\t\t\t\tbounds.y + bounds.height - 2);\n\t\t\telse\n\t\t\t\tg2.drawString(sdf.format(endtime), bounds.x + 2,\n\t\t\t\t\t\tbounds.y + bounds.height - 2);\n\t\t}\n\n\t\tg2.setClip(s);\n\t\tg2.setColor(Color.black);\n\n\t}\n\n\n\t/**\n\t * draw a string with word wrap.\n\t */\n\tprivate static void drawWrappedString(Graphics2D g2, String tx, int x, int y, int w, boolean strike) {\n\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\t\tHashMap<TextAttribute, Serializable> hm = new HashMap<TextAttribute, Serializable>();\n\t\thm.put(TextAttribute.FONT, g2.getFont());\n\t\tif( strike )\n\t\t\thm.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);\n\t\tAttributedString as = new AttributedString(tx, hm);\n\t\tAttributedCharacterIterator para = as.getIterator();\n\t\tint start = para.getBeginIndex();\n\t\tint endi = para.getEndIndex();\n\t\tLineBreakMeasurer lbm = new LineBreakMeasurer(para,\n\t\t\t\tnew FontRenderContext(null, true, false));\n\t\tlbm.setPosition(start);\n\t\tint tt = y + 2;\n\t\twhile (lbm.getPosition() < endi) {\n\t\t\tTextLayout tlayout = lbm.nextLayout(w - (2 * fontDesent));\n\t\t\ttt += tlayout.getAscent();\n\t\t\ttlayout.draw(g2, x + 2, tt);\n\t\t\ttt += tlayout.getDescent() + tlayout.getLeading();\n\t\t}\n\t}\n\n\t/**\n\t * Gets the bottom adjustment.\n\t * \n\t * @return the bottom adjustment\n\t */\n\tpublic double getBottomAdjustment() {\n\t\treturn bottom;\n\t}\n\n\t/**\n\t * Gets the box color \n\t * \t\n\t * @return the box color\n\t */\n\tprivate static Color getBoxColor() {\n\t\treturn new Color(Theme.getCurrentTheme().getDefaultBg());\n\t}\n\n\t/**\n\t * Gets the date.\n\t * \n\t * @return the date\n\t */\n\tpublic Date getDate() {\n\t\treturn date;\n\t}\n\n\t/**\n\t * Gets the left adjustment.\n\t * \n\t * @return the left adjustment\n\t */\n\tprivate double getLeftAdjustment() {\n\t\treturn left;\n\t}\n\n\t/**\n\t * Gets the max across at one time.\n\t * \n\t * @return the max across at one time\n\t */\n\tprivate int getMaxAcrossAtOneTime() {\n\t\treturn maxAcrossAtOneTime;\n\t}\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.ui.calendar.Box#getMenu()\n\t */\n\t@Override\n\tpublic JPopupMenu getMenu() {\n\t\tJMenuItem mnuitm;\n\t\tif (popmenu == null) {\n\t\t\tpopmenu = new JPopupMenu();\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Edit\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tonClick();\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Delete\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tint ret = -1;\n\t\t\t\t\tif( AppointmentModel.getReference().next_todo(appt, date) != null )\n\t\t\t\t\t{\n\t\t\t\t\t\tret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Future_Todo_Warn\"), Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Really_Delete_\") + \"?\",\n\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.WARNING_MESSAGE);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Really_Delete_\")\n\t\t\t\t\t\t\t\t+ \"?\", Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Confirm_Delete\"),\n\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\tdelete();\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Copy\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tClipBoard.getReference().put(appt.copy());\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (isTodo()) {\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Done_(No_Delete)\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().do_todo(\n\t\t\t\t\t\t\t\t\tappt.getKey(), false, date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Done_(Delete)\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().do_todo(\n\t\t\t\t\t\t\t\t\tappt.getKey(), true, date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (Repeat.isRepeating(appt)) {\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Delete_One_Only\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().delOneOnly(\n\t\t\t\t\t\t\t\t\tappt.getKey(), date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn popmenu;\n\t}\n\n\t/**\n\t * Gets the right adjustment.\n\t * \n\t * @return the right adjustment\n\t */\n\tprivate double getRightAdjustment() {\n\t\treturn right;\n\t}\n\n\t/* (non-Javadoc)\n\t * @see net.sf.borg.ui.calendar.Box#getText()\n\t */\n\t@Override\n\tpublic String getText() {\n\t\treturn displayText;\n\t}\n\n\t/**\n\t * Gets the text color.\n\t * \n\t * @return the text color\n\t */\n\tprivate String getTextColor() {\n\t\tif (appt == null)\n\t\t\treturn null;\n\n\t\tif (ApptBoxPanel.isStrike(appt, date)) {\n\t\t\treturn (\"strike\");\n\t\t}\n\t\treturn appt.getColor();\n\t}\n\n\t/**\n\t * Gets the top adjustment.\n\t * \n\t * @return the top adjustment\n\t */\n\tpublic double getTopAdjustment() {\n\t\treturn top;\n\t}\n\n\t/**\n\t * Checks if is placed.\n\t * \n\t * @return true, if is placed\n\t */\n\tprivate boolean isPlaced() {\n\t\treturn isPlaced;\n\t}\n\n\t/**\n\t * Checks if is todo.\n\t * \n\t * @return true, if is todo\n\t */\n\tprivate boolean isTodo() {\n\t\treturn appt.isTodo();\n\t}\n\n\t/**\n\t * move an appointment when the box is dragged\n\t */\n\t@Override\n\tpublic void move(int realtime, Date d) throws Exception {\n\n\t\tAppointment ap = AppointmentModel.getReference().getAppt(appt.getKey());\n\t\tint oldday = DateUtil.dayOfEpoch(ap.getDate());\n\n\t\tint hour = realtime / 60;\n\t\tint min = realtime % 60;\n\n\t\tif (realtime == -1) {\n\t\t\t// we are moving to be untimed - clear the duration\n\t\t\tap.setDuration(null);\n\t\t\tap.setUntimed(\"Y\");\n\t\t\thour = 0;\n\t\t\tmin = 0;\n\t\t}\n\n\t\t// set the new time\n\t\tGregorianCalendar newCal = new GregorianCalendar();\n\t\tnewCal.setTime(d);\n\t\tnewCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\tint roundMin = (min / 5) * 5;\n\t\tnewCal.set(Calendar.MINUTE, roundMin);\n\t\tDate newTime = newCal.getTime();\n\t\t\n\n\t\tint newday = DateUtil.dayOfEpoch(newTime);\n\t\t\n\t\t// check if the user is trying to change the date of a repeating appt\n\t\tif (oldday != newday && Repeat.isRepeating(ap)) {\n            // if date change is not the first in the sequence, move appointments to that day of the week \n\t\t\tint k2 = DateUtil.dayOfEpoch(date);\n\t\t\tif (oldday != k2) {\n\t\t\t    int incdate = newday - k2;\n                newCal.setTime(ap.getDate());\n                newCal.add(Calendar.DATE, incdate);\n                newTime = newCal.getTime();\n                \n                // move next todo by the same amount\n                Date nt = ap.getNextTodo();\n                if( nt != null )\n                {\n            \t\tGregorianCalendar newTodo = new GregorianCalendar();\n            \t\tnewTodo.setTime(nt);\n            \t\tnewTodo.add(Calendar.DATE, incdate);\n            \t\tap.setNextTodo(newTodo.getTime());\n                }\n\t\t\t}\n\t\t}\n\t\tap.setDate(newTime);\n\t\tAppointmentModel.getReference().saveAppt(ap);\n\t}\n\n\t/**\n\t * react to a mouse click on the box\n\t */\n\t@Override\n\tpublic void onClick() {\n\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(date);\n\t\t\n\t\t// create an appt list view, add it as a new tab, and set it\n\t\t// to show the box's appt\n\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal\n\t\t\t\t\t\t.get(Calendar.DATE));\n\t\tag.showView();\n\t\tag.showApp(appt.getKey());\n\n\t}\n\n\t/**\n\t * react to a box resize - change the appt duration\n\t * \n\t * @param isTop true if the top edge was dragged\n\t * @param realtime the time in minutes after midnight that the dragged edge was dragged to \n\t * \n\t * @throws Exception the exception\n\t */\n\tpublic void resize(boolean isTop, int realtime) throws Exception {\n\t\t// calculate new start hour or duration and update appt\n\n\t\tint hour = realtime / 60;\n\t\tint min = realtime % 60;\n\n\t\tif (isTop) {\n\t\t\t\n\t\t\t// get appt from DB - one cached here has time prepended to text by\n\t\t\t// Day.getDayInfo()\n\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\tappt.getKey());\n\t\t\t\n\t\t\tDate oldTime = ap.getDate();\n\t\t\t\n\t\t\t// top moved - this changes the appt start time\n\t\t\t// so determine the new time\n\t\t\tGregorianCalendar newCal = new GregorianCalendar();\n\t\t\tnewCal.setTime(oldTime);\n\t\t\tnewCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\t\tint roundMin = (min / 5) * 5;\n\t\t\tnewCal.set(Calendar.MINUTE, roundMin);\n\t\t\tDate newTime = newCal.getTime();\n\t\t\t\n\t\t\t// determine the new duration\n\t\t\tint newDur = ap.getDuration().intValue()\n\t\t\t\t\t+ ((int) (oldTime.getTime() - newTime.getTime()) / (1000 * 60));\n\t\t\t\n\t\t\t// don't allow top to be dragged onto bottom - or below\n\t\t\tif (newDur < 5)\n\t\t\t\treturn;\n\t\t\t\n\t\t\t// update appt\n\t\t\tap.setDate(newTime);\n\t\t\tap.setDuration(Integer.valueOf(newDur));\n\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t\t\n\t\t} else {\n\t\t\t// get appt from DB - one cached here has time prepended to text by\n\t\t\t// Day.getDayInfo()\n\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\tappt.getKey());\n\t\t\tDate start = ap.getDate();\n\t\t\t\n\t\t\t// remember original end time\n\t\t\tlong endtime = start.getTime() + (60 * 1000)\n\t\t\t\t\t* ap.getDuration().intValue();\n\t\t\tDate oldEnd = new Date(endtime);\n\t\t\t\n\t\t\t// bottom is being dragged - end time will change\n\t\t\tCalendar newEnd = new GregorianCalendar();\n\t\t\tnewEnd.setTime(oldEnd);\n\t\t\tnewEnd.set(Calendar.HOUR_OF_DAY, hour);\n\t\t\tint roundMin = (min / 5) * 5;\n\t\t\tnewEnd.set(Calendar.MINUTE, roundMin);\n\t\t\t\n\t\t\t\n\t\t\t// calculate new duration\n\t\t\tint newDur = (int) (newEnd.getTime().getTime() - start.getTime())\n\t\t\t\t\t/ (1000 * 60);\n\t\t\t\n\t\t\t// don't let user drag bottom over the top or above\n\t\t\tif (newDur < 5)\n\t\t\t\treturn;\n\t\t\t\n\t\t\t// update appt\n\t\t\tap.setDuration(Integer.valueOf(newDur));\n\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the bottom adjustment.\n\t * \n\t * @param bottom the new bottom adjustment\n\t */\n\tprivate void setBottomAdjustment(double bottom) {\n\t\tthis.bottom = bottom;\n\t}\n\n\t/**\n\t * Sets the left adjustment.\n\t * \n\t * @param left the new left adjustment\n\t */\n\tprivate void setLeftAdjustment(double left) {\n\t\tthis.left = left;\n\t}\n\n\t/**\n\t * Sets the max across at one time.\n\t * \n\t * @param maxAcrossAtOneTime the new max across at one time\n\t */\n\tprivate void setMaxAcrossAtOneTime(int maxAcrossAtOneTime) {\n\t\tthis.maxAcrossAtOneTime = maxAcrossAtOneTime;\n\t}\n\n\t/**\n\t * Sets the placed flag\n\t * \n\t * @param isPlaced the new placed flag value\n\t */\n\tprivate void setPlaced(boolean isPlaced) {\n\t\tthis.isPlaced = isPlaced;\n\t}\n\n\t/**\n\t * Sets the right adjustment.\n\t * \n\t * @param right the new right adjustment\n\t */\n\tprivate void setRightAdjustment(double right) {\n\t\tthis.right = right;\n\t}\n\n\t/**\n\t * Sets the top adjustment.\n\t * \n\t * @param top the new top adjustment\n\t */\n\tprivate void setTopAdjustment(double top) {\n\t\tthis.top = top;\n\t}\n\n\t@Override\n\tpublic String getToolTipText() {\n\t\treturn tooltipText;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/ApptBoxPanel.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.LabelEntity;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.awt.geom.Rectangle2D;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\n/**\n * ApptBoxPanel is the base class for Panels that act as containers for\n * Box and DateZone objects. It manages the layout of Boxes and the various operations\n * that can be done on them - such as dragging, resizing, clicking\n */\nabstract class ApptBoxPanel extends JPanel implements ComponentListener {\n\n\tprivate static final long serialVersionUID = 1L;\n\t\n\t// rectangle corner radius\n\tprivate static final int radius = 2;\n\n\t/**\n\t * ClickedBoxInfo contains information about where the mouse is and what\n\t * Box and/or DateZone it is within\n\t */\n\tstatic private class ClickedBoxInfo {\n\t\tpublic Box box = null; //box that the mouse is in\n\t\tpublic boolean onBottomBorder = false; //true if mouse is on the bottom border of a box\n\t\tpublic boolean onTopBorder = false; // true if the mouse is on the top border of a box\n\t\tpublic DateZone zone = null; // datezone that the mouse is in\n\t\tpublic boolean boxChanged = false; // true if we've changed boxes since the last check\n\t}\n\n\t/**\n\t * DragNewBox is a Box only used by ApptBoxPanel. It is the Box drawn as the\n\t * user drags the mouse to create a new, timed appointment\n\t */\n\tprivate class DragNewBox extends Box {\n\t\t\n\t\t// border thickness\n\t\tprivate final BasicStroke thicker = new BasicStroke(4.0f);\n\n\t\t// zone that the box is in\n\t\tDateZone zone;\n\n\t\t// popup menu\n\t\tJPopupMenu pop = null;\n\n\t\t/**\n\t\t * Instantiates a new drag new box.\n\t\t *\n\t\t */\n\t\tpublic DragNewBox(DateZone zone) {\n\t\t\tsuper(null, null);\n\t\t\tthis.zone = zone;\n\t\t}\n\n\t\t/**\n\t\t * Adds an appointment based on the location as size of this Box\n\t\t */\n\t\tprivate void addAppt() {\n\t\t\t\n\t\t\t// prompt for appt text\n\t\t\tString text = JOptionPane.showInputDialog(\"\", Resource\n\t\t\t\t\t.getResourceString(\"Please_enter_some_appointment_text\"));\n\t\t\tif (text == null || text.trim().isEmpty()) {\n\t\t\t\tErrmsg.getErrorHandler()\n\t\t\t\t\t\t.notice(Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Please_enter_some_appointment_text\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// get default appt values, if any from prefs\n\t\t\tAppointment appt = AppointmentModel.getDefaultAppointment();\n\n\t\t\t// get a new appt if no defaults\n\t\t\tif (appt == null) {\n\t\t\t\tappt = AppointmentModel.getReference().newAppt();\n\t\t\t}\n\n\t\t\t// set text\n\t\t\tappt.setText(text);\n\n\n\t\t\t// determine the appt time and duration based on the size of this box\n\t\t\tRectangle r = getBounds();\n\t\t\tint topmins = realMins((r.y - resizeYMin)\n\t\t\t\t\t/ (resizeYMax - resizeYMin));\n\t\t\tint botmins = realMins((r.y - resizeYMin + r.height)\n\t\t\t\t\t/ (resizeYMax - resizeYMin));\n\t\t\tint realtime = topmins;\n\t\t\tint hour = realtime / 60;\n\t\t\tint min = realtime % 60;\n\t\t\tmin = (min / 5) * 5;\n\t\t\tCalendar startCal = new GregorianCalendar();\n\t\t\tstartCal.setTime(zone.getDate());\n\t\t\tstartCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\t\tstartCal.set(Calendar.MINUTE, min);\n\t\t\tappt.setDate(startCal.getTime());\n\n\t\t\t// duration\n\t\t\tint realend = botmins;\n\t\t\tint ehour = realend / 60;\n\t\t\tint emin = realend % 60;\n\t\t\temin = (emin / 5) * 5;\n\t\t\tint dur = 60 * (ehour - hour) + emin - min;\n\t\t\tappt.setDuration(Integer.valueOf(dur));\n\n\t\t\t// set untimed if no duration\n\t\t\tif (dur > 0)\n\t\t\t\tappt.setUntimed(\"N\");\n\t\t\telse\n\t\t\t\tappt.setUntimed(\"Y\");\n\t\t\t\n\t\t\t// save appt\n\t\t\tAppointmentModel.getReference().saveAppt(appt);\n\n\t\t\t// remove the DragNewBox\n\t\t\tremoveDragNewBox();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * draw the DragNewBox - it is just a rounded rectangle outline with\n\t\t * start and end time indicators\n\t\t */\n\t\t@Override\n\t\tpublic void draw(Graphics2D g2, Component comp) {\n\t\t\t\n\t\t\t// border\n\t\t\tStroke stroke = g2.getStroke();\n\t\t\tg2.setStroke(thicker);\n\t\t\tg2.setColor(Color.GREEN);\n\t\t\tif (isSelected == true) {\n\t\t\t\tg2.setColor(Color.CYAN);\n\t\t\t}\n\t\t\tRectangle r = getBounds();\n\t\t\tg2.drawRoundRect(r.x+2, r.y, r.width-2, r.height, radius * radius,\n\t\t\t\t\tradius * radius);\n\t\t\t\n\t\t\t// start and end time indicators\n\t\t\tg2.setStroke(stroke);\n\t\t\tdouble top = (r.y - resizeYMin) / (resizeYMax - resizeYMin);\n\t\t\tdouble bot = (r.y - resizeYMin + r.height)\n\t\t\t\t\t/ (resizeYMax - resizeYMin);\n\t\t\tg2.setColor(new Color(50, 50, 50));\n\t\t\tRectangle2D bb = g2.getFont().getStringBounds(\"00:00\",\n\t\t\t\t\tg2.getFontRenderContext());\n\t\t\tg2.fillRect(r.x + 2, r.y - (int) bb.getHeight(), (int) bb\n\t\t\t\t\t.getWidth(), (int) bb.getHeight());\n\t\t\tg2.fillRect(r.x + 2, r.y + r.height - (int) bb.getHeight(),\n\t\t\t\t\t(int) bb.getWidth(), (int) bb.getHeight());\n\t\t\tg2.setColor(Color.WHITE);\n\t\t\tg2.drawString(getTimeString(top), r.x + 2, r.y - 2);\n\t\t\tg2.drawString(getTimeString(bot), r.x + 2, r.y + r.height - 2);\n\n\t\t}\n\n\t\t@Override\n\t\tpublic JPopupMenu getMenu() {\n\t\t\tif (pop == null) {\n\t\t\t\tJMenuItem mnuitm = null;\n\t\t\t\tpop = new JPopupMenu();\n\t\t\t\tpop.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Add_New\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\taddAppt();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn pop;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onClick() {\n\t\t\taddAppt();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getText() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getToolTipText() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t/**\n\t * MyMouseListener reacts to all mouse events\n\t */\n\tprivate class MyMouseListener implements MouseListener, MouseMotionListener {\n\n\t\t// during a resize, if true indicates that we are dragging the top of a\n\t\t// box, otherwise, the bottom is being dragged\n\t\tprivate boolean resizeTop = true;\n\n\t\t/**\n\t\t * mouse clicked - either show a popup menu (right button) or \n\t\t * send an onclick message to a box\n\t\t */\n\t\t@Override\n\t\tpublic void mouseClicked(MouseEvent evt) {\n\n\t\t\tevt.translatePoint(translation, translation);\n\t\t\t\n\t\t\t// get the box and/or zone clicked\n\t\t\tClickedBoxInfo b = getClickedBoxInfo(evt);\n\t\t\t\n\t\t\tif( !SwingUtilities.isLeftMouseButton(evt))\n\t\t\t\treturn;\n\n\t\t\tevt.getComponent().repaint();\n\n\t\t\t// call onClick() if we clicked on anything\n\t\t\tif (b == null)\n\t\t\t\treturn;\n\t\t\telse if (b.box != null)\n\t\t\t{\n\t\t\t\tif( b.box.clicksToActivate() <= evt.getClickCount())\n\t\t\t\t\tb.box.onClick();\n\t\t\t}\n\t\t\t\n\t\t\t//double-click to set appointment time\n\t\t\telse if (b.zone != null && evt.getClickCount() > 1 && evt.getY() >= resizeYMin)\t{\n\t\t\t\t// determine the appt time based on the location of the double-click\n\t\t\t\tint clickmins = realMins((evt.getY() - resizeYMin) / (resizeYMax - resizeYMin));\n\t\t\t\tint realtime = clickmins;\n\t\t\t\tint hour = realtime / 60;\n\t\t\t\tint min = realtime % 60;\n\t\t\t\tmin = (min / 5) * 5;\n\t\t\t\tGregorianCalendar startCal = new GregorianCalendar();\n\t\t\t\tstartCal.setTime(b.zone.getDate());\n\t\t\t\t//add double-clicked hour and minute\n\t\t\t\tstartCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\t\t\tstartCal.set(Calendar.MINUTE, min);\n\t\t\t\tb.zone.onClick(startCal);\n\t\t\t}\n\t\t\t\n\t\t\t// double-click outside of valid start time panel\n\t\t\telse if (b.zone != null && evt.getClickCount() > 1 && evt.getY() < resizeYMin)\n\t\t\t\tb.zone.onClick();\n\t\t}\n\n\t\t/**\n\t\t * mouse dragged - but not yet released\n\t\t */\n\t\t@Override\n\t\tpublic void mouseDragged(MouseEvent evt) {\n\t\t\tevt.translatePoint(translation, translation);\n\n\t\t\t// ignore right-click drag\n\t\t\tif( !SwingUtilities.isLeftMouseButton(evt))\n\t\t\t\treturn;\n\n\t\t\t// in-a-drag flag\n\t\t\tdragStarted = true;\n\t\t\t\n\t\t\t// if we are resizing a box\n\t\t\tif (resizedBox != null) {\n\t\t\t\t\n\t\t\t\t// adjust the size of the displayed resize rectangle due to the\n\t\t\t\t// mouse drag\n\t\t\t\tif (resizeTop == true) {\n\t\t\t\t\tint top = (int) Math.max(evt.getY(), resizeYMin);\n\t\t\t\t\tsetResizeBox(resizedBox.getBounds().x, top, resizedBox\n\t\t\t\t\t\t\t.getBounds().width, resizedBox.getBounds().height\n\t\t\t\t\t\t\t+ resizedBox.getBounds().y - top);\n\t\t\t\t} else {\n\t\t\t\t\tint bot = (int) Math.min(evt.getY(), resizeYMax);\n\t\t\t\t\tsetResizeBox(resizedBox.getBounds().x, resizedBox\n\t\t\t\t\t\t\t.getBounds().y, resizedBox.getBounds().width, bot\n\t\t\t\t\t\t\t- resizedBox.getBounds().y);\n\t\t\t\t}\n\t\t\t\tevt.getComponent().repaint();\n\t\t\t// if we are dragging a box around (a move)\n\t\t\t} else if (draggedBox != null) {\n\n\t\t\t\t// draw a \"move\" rectangle to show where the box is moving\n\t\t\t\t// we just use the resize box for this since that mechanism is already there\n\t\t\t\tint top = evt.getY() - (draggedBox.getBounds().height / 2);\n\t\t\t\tif (top < dragYMin)\n\t\t\t\t\ttop = (int) dragYMin;\n\t\t\t\tif (top + draggedBox.getBounds().height > dragYMax)\n\t\t\t\t\ttop = (int) dragYMax - draggedBox.getBounds().height;\n\t\t\t\tint left = evt.getX() - (draggedBox.getBounds().width / 2);\n\t\t\t\tif (left < dragXMin)\n\t\t\t\t\tleft = (int) dragXMin;\n\t\t\t\tif (left + draggedBox.getBounds().width > dragXMax)\n\t\t\t\t\tleft = (int) dragXMax - draggedBox.getBounds().width;\n\t\t\t\tsetResizeBox(left, top, draggedBox.getBounds().width,\n\t\t\t\t\t\tdraggedBox.getBounds().height);\n\n\t\t\t\tevt.getComponent().repaint();\n\t\t\t\t\n\t\t\t// if we are dragging out the outline of a new appointment\n\t\t\t} else if (draggedAnchor != -1) {\n\t\t\t\t\n\t\t\t\tClickedBoxInfo b = getClickedBoxInfo(evt);\n\t\t\t\tif( b == null )\n\t\t\t\t\treturn;\n\t\t\t\t\n\t\t\t\t// create the DragNewBox if it doesn;t yet exist\n\t\t\t\tif (dragNewBox == null)\n\t\t\t\t{\t\n\t\t\t\t\tdragNewBox = new DragNewBox(b.zone);\n\t\t\t\t\tsetDragNewBox(b.zone.getBounds().x, evt.getY(),\n\t\t\t\t\t\t\tb.zone.getBounds().width, 5);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// drag out the DragNewBox - but don't allow a drag beyond\n\t\t\t\t// resizeYMin and resizeYMax - the bounds allowed for dragging\n\t\t\t\tdouble y = evt.getY();\n\t\t\t\ty = Math.max(y, resizeYMin);\n\t\t\t\ty = Math.min(y, resizeYMax);\n\t\t\t\tRectangle r = dragNewBox.getBounds();\n\t\t\t\t\n\t\t\t\t// draw the DragNewBox above or below the starting (anchor)\n\t\t\t\t// point, depending on if we are dragging up or down\n\t\t\t\tif (y > draggedAnchor) {\n\t\t\t\t\tsetDragNewBox(r.x, r.y, r.width, y - draggedAnchor);\n\t\t\t\t} else {\n\t\t\t\t\tsetDragNewBox(r.x, y, r.width, draggedAnchor - y);\n\t\t\t\t}\n\t\t\t\tevt.getComponent().repaint();\n\t\t\t}\n\t\t}\n\n\t\t/** not used */\n\t\t@Override\n\t\tpublic void mouseEntered(MouseEvent evt) {\n\t\t  // empty\n\t\t}\n\n\t\t/** not used */\n\t\t@Override\n\t\tpublic void mouseExited(MouseEvent evt) {\n\t\t  // empty\n\t\t}\n\n\t\t/**\n\t\t * mouse moved without being pressed - do mouse-over type stuff\n\t\t */\n\t\t@Override\n\t\tpublic void mouseMoved(MouseEvent evt) {\n\t\t\tevt.translatePoint(translation, translation);\n\n\t\t\t\n\t\t\tJPanel panel = (JPanel) evt.getComponent();\n\n\t\t\t// get box or zone we are within\n\t\t\tClickedBoxInfo b = getClickedBoxInfo(evt);\n\t\t\t\n\t\t\t// set tool tip text\n\t\t\tif (b != null && b.box != null  ) {\n\t\t\t\tpanel.setToolTipText(b.box.getToolTipText());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpanel.setToolTipText(null);\n\t\t\t}\n\n\t\t\t// set the mouse cursor depending on where we are\n\t\t\t// in relation to a box - border vs. inside vs outside\n\t\t\tif (b != null && (b.onTopBorder || b.onBottomBorder)) {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));\n\t\t\t} else if (b != null && b.box != null\n\t\t\t\t\t&& b.box instanceof Box.Draggable) {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.MOVE_CURSOR));\n\t\t\t} else {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.HAND_CURSOR));\n\t\t\t}\n\t\t\t\n\t\t\t// only repaint if we have moved to a different box or zone\n\t\t\tif( b != null && b.boxChanged )\n\t\t\t\tevt.getComponent().repaint();\n\t\t}\n\n\t\t/**\n\t\t * react to mouse press and not yet released\n\t\t */\n\t\t@Override\n\t\tpublic void mousePressed(MouseEvent evt) {\n\n\t\t\tevt.translatePoint(translation, translation);\n\n\t\t\t// ignore right click\n\t\t\tif( !SwingUtilities.isLeftMouseButton(evt))\n\t\t\t\treturn;\n\n\t\t\t// get box or zone we are in\n\t\t\tClickedBoxInfo b = getClickedBoxInfo(evt);\n\t\t\t\n\t\t\t// if we press in a box other than the DragNewBox - then\n\t\t\t// get rid of the DragNewBox\n\t\t\tif (b == null || b.box != dragNewBox)\n\t\t\t\tremoveDragNewBox();\n\n\t\t\t// reset drag started (end any current drag)\n\t\t\tdragStarted = false;\n\n\t\t\t// if we are on the border of a box - start a resize\n\t\t\tif (b != null && (b.onTopBorder || b.onBottomBorder)) {\n\t\t\t\t// start resize of an appointment box\n\t\t\t\tif (b.box instanceof ApptBox) {\n\t\t\t\t\tresizedBox = (ApptBox) b.box;\n\t\t\t\t\tsetResizeBox(b.box.getBounds().x, b.box.getBounds().y,\n\t\t\t\t\t\t\tb.box.getBounds().width, b.box.getBounds().height);\n\t\t\t\t\tresizeTop = !b.onBottomBorder;\n\t\t\t\t// start resize of the drag new box\n\t\t\t\t} else if (b.box == dragNewBox) {\n\t\t\t\t\tif (b.onBottomBorder) {\n\t\t\t\t\t\tdraggedAnchor = dragNewBox.getBounds().y;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdraggedAnchor = dragNewBox.getBounds().y\n\t\t\t\t\t\t\t\t+ dragNewBox.getBounds().height;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tevt.getComponent().repaint();\n\t\t\t// start drag (move) of a Box\n\t\t\t} else if (b != null && b.box != null\n\t\t\t\t\t&& b.box instanceof Box.Draggable) {\n\t\t\t\tdraggedBox = b.box;\n\t\t\t\tsetResizeBox(b.box.getBounds().x, b.box.getBounds().y, b.box\n\t\t\t\t\t\t.getBounds().width, b.box.getBounds().height);\n\t\t\t\tevt.getComponent().repaint();\n\t\t\t// if we have pressed inside a zone, this is the start of a \n\t\t\t// DragNewBox - so set the anchor point and record the zone.\n\t\t\t// *** only do this if we are inside the resize bounds\n\t\t    // this is the magic spot where we enforce sweeping out a new item\n\t\t\t// only in certain spots\n\t\t\t} else if (b != null && b.zone != null && evt.getY() > resizeYMin\n\t\t\t\t\t&& evt.getY() < resizeYMax) {\n\t\t\t\tdraggedAnchor = evt.getY();\n\t\t\t}\n\n\t\t\t// set the mouse cursor depending on where we are\n\t\t\t// in relation to a box - border vs. inside vs outside\n\t\t\tJPanel panel = (JPanel) evt.getComponent();\n\t\t\tif (b != null && (b.onTopBorder || b.onBottomBorder)) {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));\n\t\t\t} else if (b != null && b.box != null\n\t\t\t\t\t&& b.box instanceof Box.Draggable) {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.MOVE_CURSOR));\n\t\t\t} else {\n\t\t\t\tpanel.setCursor(new Cursor(Cursor.HAND_CURSOR));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * mouse has been released after a press and possible drag\n\t\t */\n\t\t@Override\n\t\tpublic void mouseReleased(MouseEvent evt) {\n\t\t\tevt.translatePoint(translation, translation);\n\n\t\t\t// get the box and/or zone clicked\n\t\t\tClickedBoxInfo b = getClickedBoxInfo(evt);\n\t\t\t// right click\n\t\t\tif (evt.getButton() == MouseEvent.BUTTON3) {\n\n\t\t\t\tif( dragStarted && draggedBox != null)\n\t\t\t\t\treturn;\n\n\t\t\t\t// if no box or zone - do nothing\n\t\t\t\tif (b == null)\n\t\t\t\t\treturn;\n\n\t\t\t\t// show a box's menu\n\t\t\t\tif (b.box != null) {\n\t\t\t\t\tif (b.box.getMenu() != null) {\n\t\t\t\t\t\tb.box.getMenu().show(evt.getComponent(), evt.getX(),\n\t\t\t\t\t\t\t\tevt.getY());\n\t\t\t\t\t}\n\t\t\t\t\t// show a zone's menu\n\t\t\t\t} else if (b.zone != null) {\n\t\t\t\t\tb.zone.getMenu().show(evt.getComponent(), evt.getX(),\n\t\t\t\t\t\t\tevt.getY());\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// if we have been resizing, then send a resize call to\n\t\t\t// the resized box so it can deal with the resize\n\t\t\tif (resizedBox != null && dragStarted) {\n\t\t\t\tdouble y = evt.getY();\n\t\t\t\ty = Math.max(y, resizeYMin);\n\t\t\t\ty = Math.min(y, resizeYMax);\n\t\t\t\ttry {\n\t\t\t\t\tresizedBox.resize(resizeTop, realMins((y - resizeYMin)\n\t\t\t\t\t\t\t/ (resizeYMax - resizeYMin)));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t// if we have been moving a box, then call its move method\n\t\t\t// so that it can deal with it\n\t\t\t} else if (draggedBox != null && dragStarted) {\n\t\t\t\tdouble y = resizeRectangle.y;\n\t\t\t\ty = Math.max(y, resizeYMin);\n\t\t\t\ty = Math.min(y, resizeYMax);\n\n\t\t\t\tdouble centerx = evt.getX();\n\t\t\t\tdouble centery = evt.getY();\n\t\t\t\t\n\t\t\t\tcentery = Math.max(centery, dragYMin);\n\t\t\t\tcentery = Math.min(centery, dragYMax);\n\t\t\t\tDate d = getDateForCoord(centerx, centery);\n\t\t\t\ttry {\n\t\t\t\t\t// if we moved the box inside the resize area, send it the\n\t\t\t\t\t// new start time info\n\t\t\t\t\tif (isInsideResizeArea(resizeRectangle.y, resizeRectangle.y\n\t\t\t\t\t\t\t+ resizeRectangle.height)) {\n\t\t\t\t\t\t((Box.Draggable) draggedBox).move(\n\t\t\t\t\t\t\t\trealMins((y - resizeYMin)\n\t\t\t\t\t\t\t\t\t\t/ (resizeYMax - resizeYMin)), d);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we moved the box outside of the resize area, so \n\t\t\t\t\t\t// send no time info (the resize area == the time grid)\n\t\t\t\t\t\t((Box.Draggable) draggedBox).move(-1, d);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// reset resizing and dragging\n\t\t\tdraggedBox = null;\n\t\t\tresizedBox = null;\n\t\t\tremoveResizeBox();\n\t\t\tevt.getComponent().repaint();\n\n\t\t}\n\t}\n\t\n\t// format for time markers on resize/drag box\n\tprivate final SimpleDateFormat sdf = new SimpleDateFormat(\"hh:mm\");\n\tprivate final SimpleDateFormat sdf24 = new SimpleDateFormat(\"HH:mm\");\n\n\n\t// to adjust, since since Graphics2D is translated\n\tfinal static private int translation = -10; \n\n\t/**\n\t * Checks if entity should be shown as strike-through on a certain date.\n\t * \n\t * @param appt the entity\n\t * @param date the date\n\t * \n\t * @return true, if is strike\n\t */\n\tpublic static boolean isStrike(CalendarEntity appt, Date date) {\n\t\t//if( appt.getNextTodo()!= null)\n\t\t//\tSystem.out.println(appt.getText() + \" \" + appt.getNextTodo() + \" \" + date);\n\t\treturn (appt.getColor() != null && appt.getColor().equals(\"strike\"))\n\t\t\t\t|| (appt.isTodo() && !(appt.getNextTodo() == null ||\n\t\t\t\t(DateUtil.dayOfEpoch(date) >= DateUtil.dayOfEpoch(appt.getNextTodo()))));\n\t\t\t\t//!appt.getNextTodo().after(date)));\n\t}\n\n\t/** the Boxes managed by this container */\n\tprotected Collection<Box> boxes = new ArrayList<Box>();\n\t\n\t/** current box we are in - to limit repaints */\n\tprivate Box currentBox = null;\n\n\t// position of drag start\n\tprivate int draggedAnchor = -1;\n\n\t// Box that is currently being dragged around\n\tprivate Box draggedBox = null;\n\n\t// Box that shows a brand new timed appt being dragged out\n\tprivate DragNewBox dragNewBox = null;\n\n\t// flag to indicate we have started dragging\n\tprivate boolean dragStarted = false;\n\n\t// bounds which limit where a box can be dragged\n\tprivate double dragXMax = 0;\n\tprivate double dragXMin = 0;\n\tprivate double dragYMax = 0;\n\tprivate double dragYMin = 0;\n\n\t// end time of the timed zone\n\tprotected double endmin = 0;\n\n\t// box that is being resized\n\tprivate ApptBox resizedBox = null;\n\n\t// rectangle to show a resize or move in progress\n\tprivate Rectangle resizeRectangle = null;\n\n\t// bounds which limit where resizing can occur - also limits\n\t// where new appts can be dragged out\n\tprivate double resizeYMax = 0;\n\tprivate double resizeYMin = 0;\n\n\t// start time of the timed zone\n\tprotected double startmin = 0;\n\n\t// DateZones managed by this container\n\tprivate final Collection<DateZone> zones = new ArrayList<DateZone>();\n\t\n\t/**\n\t * Instantiates a new appt box panel.\n\t */\n\tpublic ApptBoxPanel() {\n\n\t\t// set up event listeners\n\t\tMyMouseListener myOneListener = new MyMouseListener();\n\t\taddMouseListener(myOneListener);\n\t\taddMouseMotionListener(myOneListener);\n\t\taddComponentListener(this);\n\n\t}\n\n\t/**\n\t * Adds an appointment to the container. Creates an ApptBox to contain it\n\t * \n\t * @param d the date of the box - not aleays the appt date\n\t * @param ap the appointment\n\t * @param bounds the bounds\n\t * @param clip the clip\n\t */\n\tprotected void addApptBox(Date d, Appointment ap, Rectangle bounds,\n\t\t\tRectangle clip) {\n\n\t\tif (Prefs.getBoolPref(PrefName.HIDESTRIKETHROUGH)\n\t\t\t\t&& ApptBoxPanel.isStrike(ap, d))\n\t\t\treturn;\n\t\tApptBox b = new ApptBox(d, ap, bounds, clip);\n\n\t\tboxes.add(b);\n\t}\n\n\t/**\n\t * Adds a date zone to this container\n\t * \n\t * @param d the date\n\t * @param bounds the bounds of the zone\n\t */\n\tpublic void addDateZone(Date d, Rectangle bounds) {\n\t\tDateZone b = new DateZone(d, bounds);\n\t\tzones.add(b);\n\t}\n\n\t/**\n\t * Adds the note box to this container\n\t * \n\t * @param d the date\n\t * @param ap the calendar entity\n\t * @param bounds the bounds\n\t * @param clip the clip\n\t * \n\t * @return the box\n\t */\n\tpublic Box addNoteBox(Date d, CalendarEntity ap, Rectangle bounds,\n\t\t\tRectangle clip) {\n\n\t\t// ignore the note box if it is strike-through and the option\n\t\t// to hide strike through is set\n\t\tif (Prefs.getBoolPref(PrefName.HIDESTRIKETHROUGH)\n\t\t\t\t&& ApptBoxPanel.isStrike(ap, d))\n\t\t\treturn null;\n\n\t\tBox b;\n\t\tif (ap instanceof LabelEntity) {\n\t\t\t// phony holiday appt added by Day object\n\t\t\tb = new LabelBox((LabelEntity) ap, bounds, clip);\n\t\t} else {\n\t\t\tb = new NoteBox(d, ap, bounds, clip);\n\t\t}\n\t\t\n\t\tboxes.add(b);\n\n\t\treturn b;\n\t}\n\n\t/**\n\t * Clear boxes and zones.\n\t */\n\tpublic void clearBoxes() {\n\t\tboxes.clear();\n\t\tzones.clear();\n\t}\n\n\t/**\n\t * Draw boxes.\n\t * \n\t * @param g2 the Graphics to draw in\n\t */\n\tpublic void drawBoxes(Graphics2D g2) {\n\n\t\t// draw each box\n\t\tfor(Box b : boxes) {\n\t\t\tb.draw(g2, this);\n\t\t}\n\n\t\t// draw the resize rectangle if needed\n\t\tif (resizeRectangle != null) {\n\t\t\tg2.setColor(Color.RED);\n\t\t\tg2.drawRoundRect(resizeRectangle.x, resizeRectangle.y,\n\t\t\t\t\tresizeRectangle.width, resizeRectangle.height, radius\n\t\t\t\t\t\t\t* radius, radius * radius);\n\n\t\t\t// draw time indicators\n\t\t\tif (isInsideResizeArea(resizeRectangle.y, resizeRectangle.y\n\t\t\t\t\t+ resizeRectangle.height)) {\n\t\t\t\tdouble top = (resizeRectangle.y - resizeYMin)\n\t\t\t\t\t\t/ (resizeYMax - resizeYMin);\n\t\t\t\tdouble bot = (resizeRectangle.y - resizeYMin + resizeRectangle.height)\n\t\t\t\t\t\t/ (resizeYMax - resizeYMin);\n\t\t\t\tg2.setColor(new Color(50, 50, 50));\n\t\t\t\tRectangle2D bb = g2.getFont().getStringBounds(\"00:00\",\n\t\t\t\t\t\tg2.getFontRenderContext());\n\t\t\t\tg2.fillRect(resizeRectangle.x + 2, resizeRectangle.y\n\t\t\t\t\t\t- (int) bb.getHeight(), (int) bb.getWidth(), (int) bb\n\t\t\t\t\t\t.getHeight());\n\t\t\t\tg2.fillRect(resizeRectangle.x + 2, resizeRectangle.y\n\t\t\t\t\t\t+ resizeRectangle.height - (int) bb.getHeight(),\n\t\t\t\t\t\t(int) bb.getWidth(), (int) bb.getHeight());\n\t\t\t\tg2.setColor(Color.WHITE);\n\t\t\t\t\n\t\t\t\t// if we are dragging or resizing an appt, then force the calculation of the time strings based on the\n\t\t\t\t// appt to avoid some rounding errors that occur when calculating times from the pixels on the screen\t\t\t\t\n\t\t\t\tif( draggedBox != null && draggedBox instanceof ApptBox)\n\t\t\t\t{\n\t\t\t\t\tApptBox draggedApptBox = (ApptBox) draggedBox;\n\t\t\t\t\tif( dragStarted == false )\n\t\t\t\t\t{\n\t\t\t\t\t\t// we haven't moved yet, so use the appt start and end\n\t\t\t\t\t\ttop = draggedApptBox.getTopAdjustment();\n\t\t\t\t\t\tbot = draggedApptBox.getBottomAdjustment();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// when moving an appt, always make sure that the bottom reflects the appt duration properly\n\t\t\t\t\t\tbot = top + draggedApptBox.getBottomAdjustment() - draggedApptBox.getTopAdjustment();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if( resizedBox != null )\n\t\t\t\t{\n\t\t\t\t\tApptBox resizedApptBox = resizedBox;\n\t\t\t\t\tif( dragStarted == false )\n\t\t\t\t\t{\n\t\t\t\t\t\t// we haven't moved yet, so use the appt start and end\n\t\t\t\t\t\ttop = resizedApptBox.getTopAdjustment();\n\t\t\t\t\t\tbot = resizedApptBox.getBottomAdjustment();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tg2.drawString(getTimeString(top), resizeRectangle.x + 2,\n\t\t\t\t\t\tresizeRectangle.y - 2);\n\t\t\t\tg2.drawString(getTimeString(bot), resizeRectangle.x + 2,\n\t\t\t\t\t\tresizeRectangle.y + resizeRectangle.height - 2);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// draw drag new box if needed\n\t\tif (dragNewBox != null) {\n\t\t\tdragNewBox.draw(g2, this);\n\t\t}\n\t\tg2.setColor(Color.black);\n\n\t}\n\n\t/**\n\t * Gets info on which zone and/or box the mouse is in - plus if we are\n\t * on the top or bottom border of the box\n\t * \n\t * @param evt the MouseEvent\n\t * \n\t * @return the ClickedBoxInfo\n\t */\n\tprivate ClickedBoxInfo getClickedBoxInfo(MouseEvent evt) {\n\t\t\n\t\tboolean onTopBorder = false;\n\t\tboolean onBottomBorder = false;\n\t\tClickedBoxInfo ret = new ClickedBoxInfo();\n\n\t\t// check if we are in the drag new box\n\t\tif (dragNewBox != null) {\n\t\t\tRectangle r = dragNewBox.getBounds();\n\t\t\tif (evt.getX() > r.x && evt.getX() < (r.x + r.width)\n\t\t\t\t\t&& evt.getY() > r.y && evt.getY() < (r.y + r.height)) {\n\t\t\t\tret.box = dragNewBox;\n\t\t\t\tdragNewBox.setSelected(true);\n\n\t\t\t\tif (Math.abs(evt.getY() - r.y) < 4) {\n\t\t\t\t\tonTopBorder = true;\n\t\t\t\t} else if (Math.abs(evt.getY() - (r.y + r.height)) < 4) {\n\t\t\t\t\tonBottomBorder = true;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tdragNewBox.setSelected(false);\n\t\t\t}\n\t\t}\n\n\t\t// check if we are in any boxes (drag new box above takes priority)\n\t\tfor(Box b : boxes){\n\t\t\t\n\t\t\tif( !b.isClickable())\n\t\t\t\tcontinue;\n\t\t\t\n\t\t\tif (ret.box == null && evt.getX() > b.getBounds().x\n\t\t\t\t\t&& evt.getX() < (b.getBounds().x + b.getBounds().width)\n\t\t\t\t\t&& evt.getY() > b.getBounds().y\n\t\t\t\t\t&& evt.getY() < (b.getBounds().y + b.getBounds().height)) {\n\n\t\t\t\tb.setSelected(true);\n\t\t\t\tret.box = b;\n\t\t\t\tif (b instanceof ApptBox) {\n\t\t\t\t\tif (Math.abs(evt.getY() - b.getBounds().y) < 4) {\n\t\t\t\t\t\tonTopBorder = true;\n\t\t\t\t\t} else if (Math.abs(evt.getY()\n\t\t\t\t\t\t\t- (b.getBounds().y + b.getBounds().height)) < 4) {\n\t\t\t\t\t\tonBottomBorder = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tb.setSelected(false);\n\t\t\t}\n\t\t}\n\n\t\t// checl if we are in a date zone\n\t\tfor( DateZone b : zones ) {\n\n\t\t\tif (evt.getX() > b.getBounds().x\n\t\t\t\t\t&& evt.getX() < (b.getBounds().x + b.getBounds().width)\n\t\t\t\t\t&& evt.getY() > b.getBounds().y\n\t\t\t\t\t&& evt.getY() < (b.getBounds().y + b.getBounds().height)) {\n\t\t\t\tret.zone = b;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tret.onTopBorder = onTopBorder;\n\t\tret.onBottomBorder = onBottomBorder;\n\t\t\n\t\tif( ret.box != currentBox )\n\t\t\tret.boxChanged = true;\n\t\tcurrentBox = ret.box;\n\t\t\n\t\tif( ret.box == null && ret.zone == null )\n\t\t\treturn null;\n\n\t\treturn ret;\n\n\t}\n\n\t/**\n\t * Gets the date for a mouse coordinate\n\t * \n\t * @param x the x coordinate\n\t * @param y the y coordinate\n\t * \n\t * @return the date for coord\n\t */\n\tabstract Date getDateForCoord(double x, double y);\n\n\t/**\n\t * Gets the time string for a given y coordinate\n\t * \n\t * @param y_fraction the y_fraction\n\t * \n\t * @return the time string\n\t */\n\tpublic String getTimeString(double y_fraction) {\n\n\t\tint realtime = realMins(y_fraction);\n\t\tint hour = realtime / 60;\n\t\tint min = realtime % 60;\n\t\tGregorianCalendar newCal = new GregorianCalendar();\n\t\tnewCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\tnewCal.set(Calendar.MINUTE, min);\n\t\tDate newTime = newCal.getTime();\n\t\tif( Prefs.getBoolPref(PrefName.MILTIME))\n\t\t\treturn sdf24.format(newTime);\n\t\treturn sdf.format(newTime);\n\t}\n\n\t/**\n\t * Checks if a box is completely inside the resize area.\n\t * \n\t * @param top the top\n\t * @param bot the bottom\n\t * \n\t * @return true, if is inside resize area\n\t */\n\tprivate boolean isInsideResizeArea(int top, int bot) {\n\t\treturn top >= resizeYMin && bot <= resizeYMax;\n\n\t}\n\n\t/**\n\t * get the time as minutes past midnight for a fraction representing\n\t * how far we are between start and end of the time zone\n\t * \n\t * @param y_fraction the y_fraction\n\t * \n\t * @return the time as minutes past midnight\n\t */\n\tprivate int realMins(double y_fraction) {\n\t\tdouble realtime = startmin + (endmin - startmin) * y_fraction;\n\t\t// round it because the double math is causing errors when later\n\t\t// converting to int\n\t\tint min = 5 * (int) Math.round(realtime / 5);\n\t\treturn min;\n\t}\n\n\t/**\n\t * Refresh.\n\t */\n\tpublic abstract void refresh();\n\n\t/**\n\t * Removes the drag new box.\n\t */\n\tprotected void removeDragNewBox() {\n\t\tdragNewBox = null;\n\t\tdraggedAnchor = -1;\n\t}\n\n\t/**\n\t * Removes the resize box.\n\t */\n\tpublic void removeResizeBox() {\n\t\tresizeRectangle = null;\n\t}\n\n\t/**\n\t * Sets the drag bounds to limit where items can be dragged.\n\t * \n\t * @param ymin the y minimum\n\t * @param ymax the y maximum\n\t * @param xmin the x minimum\n\t * @param xmax the x maximum\n\t */\n\tprotected void setDragBounds(int ymin, int ymax, int xmin, int xmax) {\n\t\tdragYMin = ymin;\n\t\tdragYMax = ymax;\n\t\tdragXMin = xmin;\n\t\tdragXMax = xmax;\n\t}\n\n\t/**\n\t * Sets the bounds for the drag new box\n\t * \n\t * @param x the x coord\n\t * @param y the y coord\n\t * @param w the width\n\t * @param h the height\n\t */\n\tprotected void setDragNewBox(double x, double y, double w, double h) {\n\n\t\tRectangle bounds = new Rectangle();\n\t\tbounds.x = (int) x;\n\t\tbounds.y = (int) y;\n\t\tbounds.height = (int) h;\n\t\tbounds.width = (int) w;\n\t\tdragNewBox.setBounds(bounds);\n\n\t}\n\n\t/**\n\t * Sets the resize bounds which limit where a resized object border can be dragged and\n\t * where a drag new box can be started\n\t * \n\t * @param ymin the y minimum\n\t * @param ymax the y maximum\n\t */\n\tprotected void setResizeBounds(int ymin, int ymax) {\n\t\tresizeYMin = ymin;\n\t\tresizeYMax = ymax;\n\t}\n\n\t/**\n\t * Sets the resize box bounds.\n\t * \n\t * @param x the x coord\n\t * @param y the y coord\n\t * @param w the width\n\t * @param h the height\n\t */\n\tprotected void setResizeBox(double x, double y, double w, double h) {\n\t\tif (resizeRectangle == null)\n\t\t\tresizeRectangle = new Rectangle();\n\t\tresizeRectangle.x = (int) x;\n\t\tresizeRectangle.y = (int) y;\n\t\tresizeRectangle.height = (int) h;\n\t\tresizeRectangle.width = (int) w;\n\n\t}\n\t\n\t@Override\n\tpublic void componentHidden(ComponentEvent arg0) {\n\t  // empty\n\t}\n\n\t@Override\n\tpublic void componentMoved(ComponentEvent e) {\n\t  // empty\n\t}\n\n\t@Override\n\tpublic void componentResized(ComponentEvent e) {\n\t\trefresh();\n\t}\n\n\t@Override\n\tpublic void componentShown(ComponentEvent e) {\n\t  // empty\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/Box.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Date;\n\n/**\n * Interface for objects that appear as boxes on the day/week/month UIs\n */\nabstract public class Box {\n\n\t/**\n\t * Interface implemented by Boxes that can be dragged\n\t */\n\tinterface Draggable {\n\n\t\t/**\n\t\t * called when object has been moved\n\t\t * \n\t\t * @param realtime\n\t\t *            time of day in minutes to which the object has been\n\t\t *            dragged. -1 indicates object dragged off of the time grid\n\t\t * @param d\n\t\t *            date that the object was dragged to\n\t\t * @throws Exception\n\t\t */\n        void move(int realtime, Date d) throws Exception;\n\t}\n\n\tprotected Rectangle bounds, clip;\n\n\tprotected boolean isSelected = false;\n\n\t/**\n\t * constructor.\n\t * \n\t * @param bounds\n\t *            box bounds\n\t * @param clip\n\t *            box clip\n\t */\n\tpublic Box(Rectangle bounds, Rectangle clip) {\n\t\tthis.bounds = bounds;\n\t\tthis.clip = clip;\n\t}\n\n\t/**\n\t * delete the box\n\t */\n\tpublic void delete() {\n\t\t// empty\n\t}\n\n\t/**\n\t * draw the box\n\t * \n\t * @param g2\n\t *            the Graphics2D to draw in\n\t * @param comp\n\t *            that contains the Graphics2D\n\t */\n\tpublic abstract void draw(Graphics2D g2, Component comp);\n\n\t/**\n\t * action called when the box is clicked\n\t */\n\tpublic abstract void onClick();\n\n\t/**\n\t * how many clicks are required to activate this box - default is\n\t * double-click\n\t * \n\t * @return clicks required to activate the box\n\t */\n\tpublic int clicksToActivate() {\n\t\treturn 2;\n\t}\n\n\t/**\n\t * get bounds\n\t * \n\t * @return bounds\n\t */\n\tpublic Rectangle getBounds() {\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * get the box popup menu\n\t * \n\t * @return the popup menu\n\t */\n\tpublic JPopupMenu getMenu() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * get the box text\n\t * \n\t * @return the text\n\t */\n\tabstract public String getText();\n\n\t/**\n\t * set bounds\n\t * \n\t * @param bounds\n\t *            new bounds\n\t */\n\tpublic void setBounds(Rectangle bounds) {\n\t\tthis.bounds = bounds;\n\t\tif (clip == null)\n\t\t\tclip = bounds;\n\t}\n\n\t/**\n\t * set selected\n\t * \n\t * @param isSelected\n\t *            new selected value\n\t */\n\tpublic void setSelected(boolean isSelected) {\n\t\tthis.isSelected = isSelected;\n\t}\n\n\t/**\n\t * gets the tool tip text for this box\n\t * \n\t * @return the tool tip text\n\t */\n\tabstract public String getToolTipText();\n\n\t/**\n\t * returns true if the box is clickable\n\t * \n\t * @return\n\t */\n\tpublic boolean isClickable() {\n\n\t\t// if a box is completely clipped, then it cannot be clicked\n        return clip == null || bounds == null || bounds.intersects(clip);\n    }\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/ButtonBox.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.model.Theme;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Date;\n\n/**\n * base class for a box that represents a clickable label on the calendar ui\n * sub-classes must implement the onClick method. used for items such as the\n * date and week buttons that let the user navigate to other views\n * \n */\npublic abstract class ButtonBox extends Box {\n\n\tprivate final Date date; // date that the box is associated with\n\n\tprivate Icon icon_ = null; // optinal icon for the label\n\n\tprivate Color backg = null; // background color\n\n\tprivate String text = null; // button text\n\n\t/**\n\t * constructor\n\t * \n\t * @param d\n\t *            date\n\t * @param text\n\t *            label text\n\t * @param icon\n\t *            icon or null\n\t * @param bounds\n\t *            bounds\n\t * @param clip\n\t *            clip\n\t */\n\tpublic ButtonBox(Date d, String text, Icon icon, Rectangle bounds,\n\t\t\tRectangle clip) {\n\t\tsuper(bounds, clip);\n\t\tdate = d;\n\t\tthis.text = text;\n\t\tthis.icon_ = icon;\n\t}\n\n\t/**\n\t * constructor\n\t * \n\t * @param d\n\t *            date\n\t * @param text\n\t *            label text\n\t * @param icon\n\t *            icon or null\n\t * @param bounds\n\t *            bounds\n\t * @param clip\n\t *            clip\n\t * @param b\n\t *            background color\n\t */\n\tpublic ButtonBox(Date d, String text, Icon icon, Rectangle bounds,\n\t\t\tRectangle clip, Color b) {\n\t\tsuper(bounds, clip);\n\t\tdate = d;\n\t\tthis.text = text;\n\t\tthis.icon_ = icon;\n\t\tthis.backg = b;\n\t}\n\n\t/**\n\t * get the date\n\t * \n\t * @return the date\n\t */\n\tpublic Date getDate() {\n\t\treturn date;\n\t}\n\n\t/**\n\t * draw the button box\n\t */\n\t@Override\n\tpublic void draw(Graphics2D g2, Component comp) {\n\n\t\tTheme t = Theme.getCurrentTheme();\n\n\t\tShape s = g2.getClip();\n\t\tif (clip != null)\n\t\t\tg2.setClip(clip);\n\n\t\tif (isSelected == true) {\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\t\t\tg2.fillRect(bounds.x, bounds.y + 2, bounds.width, bounds.height);\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t} else if (backg != null) {\n\t\t\tg2.setColor(backg);\n\t\t\tg2.fillRect(bounds.x, bounds.y + 2, bounds.width, bounds.height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\t\t} else {\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(bounds.x, bounds.y + 2, bounds.width, bounds.height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\t\t}\n\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\n\t\tg2.drawString(text, bounds.x + 2, bounds.y + smfontHeight);\n\t\tif (icon_ != null)\n\t\t\ticon_.paintIcon(null, g2, bounds.x + bounds.width - 16, bounds.y);\n\n\t\tg2.setClip(s);\n\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn text;\n\t}\n\n\t@Override\n\tpublic String getToolTipText() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int clicksToActivate() {\n\t\treturn 1;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/DateZone.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.ui.ClipBoard;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionListener;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * A DateZone is used to mark a rectagular area on the calendar UIs that\n * corresponds to a particular date. It provides the popup menu and the on-click\n * action that occurs when the user clicks on a date, but outside of any other\n * calendar items\n */\nclass DateZone {\n\n\t// the bounds of the zone\n\tprivate Rectangle bounds;\n\n\t// the date that the zone represents\n\tprivate Date date;\n\n\t// the popup menu\n\tprivate JPopupMenu popmenu = null;\n\tprivate JMenuItem pasteItem = null;\n\n\t/**\n\t * constructor.\n\t * \n\t * @param d\n\t *            the date that this zone represents\n\t * @param bounds\n\t *            the bounds of this zone\n\t */\n\tpublic DateZone(Date d, Rectangle bounds) {\n\t\tthis.date = d;\n\t\tthis.bounds = bounds;\n\t}\n\n\t/**\n\t * Gets the bounds.\n\t * \n\t * @return the bounds\n\t */\n\tpublic Rectangle getBounds() {\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * Gets the date.\n\t * \n\t * @return the date\n\t */\n\tpublic Date getDate() {\n\t\treturn date;\n\t}\n\n\t/**\n\t * Gets the popup menu.\n\t * \n\t * @return the popup menu\n\t */\n\tpublic JPopupMenu getMenu() {\n\t\tJMenuItem mnuitm;\n\t\tif (popmenu == null) {\n\t\t\tpopmenu = new JPopupMenu();\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Add_New\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tonClick();\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"todoquickentry\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tquickAdd(true);\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Quick_Note\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tquickAdd(false);\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(pasteItem = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Paste\")));\n\t\t\tpasteItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tAppointment appt = (Appointment) ClipBoard.getReference()\n\t\t\t\t\t\t\t.get(Appointment.class);\n\t\t\t\t\tpasteAppt(appt);\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\n\t\tpasteItem\n\t\t\t\t.setEnabled(ClipBoard.getReference().get(Appointment.class) != null);\n\t\treturn popmenu;\n\t}\n\n\t/**\n\t * take action on a user mouse double-click, opens appt editor for new appt\n\t */\n\tvoid onClick() {\n\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(date);\n\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n\t\t\t\tcal.get(Calendar.DATE));\n\t\tag.showView();\n\t\tag.showApp(-1);\n\t}\n\n\t/**\n\t * take action on a user mouse double-click on time interval open an appt\n\t * editor for a new appt with preset start hour and minute\n\t */\n\tvoid onClick(GregorianCalendar cal) {\n\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n\t\t\t\tcal.get(Calendar.DATE));\n\t\t// set double-click time\n\t\tag.setTime(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));\n\t\tag.showView();\n\t\tag.showApp(-1);\n\t}\n\n\t/**\n\t * quick add menu action - prompt for text and create an appt based on\n\t * default values\n\t * \n\t * @param todo\n\t *            - true if added appt should be a todo\n\t */\n\tprivate void quickAdd(boolean todo) {\n\n\t\t// prompt for todo text\n\t\tString tdtext = JOptionPane.showInputDialog(\"\", Resource\n\t\t\t\t.getResourceString(\"Please_enter_some_appointment_text\"));\n\t\tif (tdtext == null || tdtext.trim().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// load up a default appt from any saved prefs\n\t\tAppointment appt = AppointmentModel\n\t\t\t\t.getDefaultAppointment();\n\t\tif (appt == null)\n\t\t\tappt = AppointmentModel.getReference().newAppt();\n\n\t\t// set the appt date. leave the appt untimed\n\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(date);\n\t\tcal.set(Calendar.HOUR, 0);\n\t\tcal.set(Calendar.MINUTE, 0);\n\t\tcal.set(Calendar.SECOND, 0);\n\t\tcal.set(Calendar.AM_PM, Calendar.AM);\n\t\tappt.setDate(cal.getTime());\n\t\tappt.setText(tdtext);\n\t\tappt.setTodo(todo);\n\t\tappt.setUntimed(\"Y\");\n\n\t\tAppointmentModel.getReference().saveAppt(appt);\n\n\t}\n\n\tprivate void pasteAppt(Appointment appt) {\n\t\tGregorianCalendar newcal = new GregorianCalendar();\n\t\tnewcal.setTime(date);\n\n\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(appt.getDate());\n\t\tcal.set(Calendar.YEAR, newcal.get(Calendar.YEAR));\n\t\tcal.set(Calendar.MONTH, newcal.get(Calendar.MONTH));\n\t\tcal.set(Calendar.DATE, newcal.get(Calendar.DATE));\n\n\t\tString freq = appt.getFrequency();\n\n\t\ttry {\n\t\t\tif (!Repeat.isCompatible(cal,\n\t\t\t\t\tRepeat.getFreqString(Repeat.getFreq(freq)),\n\t\t\t\t\tRepeat.getDaylist(freq)))\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"recur_compat\"));\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t\tappt.setDate(cal.getTime());\n\n\t\tappt.setKey(-1);\n\t\tappt.setUid(null);\n\t\tAppointmentModel.getReference().saveAppt(appt);\n\n\t}\n\n\t/**\n\t * Sets the bounds.\n\t * \n\t * @param bounds\n\t *            the new bounds\n\t */\n\tpublic void setBounds(Rectangle bounds) {\n\t\tthis.bounds = bounds;\n\t}\n\n\t/**\n\t * Sets the date.\n\t * \n\t * @param date\n\t *            the new date\n\t */\n\tpublic void setDate(Date date) {\n\t\tthis.date = date;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/DayPanel.java",
    "content": "/*\n * This file is part of BORG.\n * \n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n * \n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n * \n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.BasicStroke;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GridBagConstraints;\nimport java.awt.Rectangle;\nimport java.awt.RenderingHints;\nimport java.awt.Shape;\nimport java.awt.Stroke;\nimport java.awt.Toolkit;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseWheelEvent;\nimport java.awt.event.MouseWheelListener;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.List;\n\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.PrintHelper;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Day;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.CalendarModule;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.NavPanel;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * DayPanel is the UI for a single day. It consists of a Navigator attached to a\n * DaySubPanel\n */\npublic class DayPanel extends DockableView implements Printable, CalendarModule {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * DaySubPanel is the Panel that shows the items for a day with a section\n\t * for untimed items and a time-grid for timed items\n\t */\n\tprivate class DaySubPanel extends ApptBoxPanel implements\n\t\t\tNavPanel.Navigator, Prefs.Listener, Printable, Model.Listener,\n\t\t\tMouseWheelListener {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\t\t// set up dash line stroke for time-grid divisions\n\t\tprivate final float[] dash1 = { 1.0f, 3.0f };\n\t\tprivate final BasicStroke dashed = new BasicStroke(0.02f,\n\t\t\t\tBasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 3.0f, dash1, 0.0f);\n\n\t\t// date being shown\n\t\tprivate int date_;\n\t\tprivate int month_;\n\t\tprivate int year_;\n\n\t\t// portion of the screen taken up by the non-timed section\n\t\tprivate double nonTimedPortion = 1.0 / 6.0;\n\n\t\t// flag to indicate if we need to reload from the model. If false, we\n\t\t// can just redraw\n\t\t// from cached data\n\t\tprivate boolean needLoad = true;\n\n\t\t// records the last date on which a draw took place - for handling the\n\t\t// first redraw after midnight\n\t\tprivate int lastDrawDate = -1;\n\n\t\t// background color\n\t\tprivate Color backgroundColor = null;\n\n\t\t// zoom factor\n\t\tprivate int zoom = 0;\n\n\t\t/**\n\t\t * Instantiates a new day sub panel.\n\t\t * \n\t\t */\n\t\tpublic DaySubPanel() {\n\n\t\t\t// refresh if prefs change\n\t\t\tPrefs.addListener(this);\n\n\t\t\t// react to mouse wheel activity\n\t\t\taddMouseWheelListener(this);\n\n\t\t\t// refresh if the appt or task models change\n\t\t\tAppointmentModel.getReference().addListener(this);\n\t\t\tTaskModel.getReference().addListener(this);\n\t\t\tAddressModel.getReference().addListener(this);\n\t\t\tSubscribedCalendars.getReference().addListener(this);\n\n\n\t\t\tgoTo(new GregorianCalendar());\n\n\t\t}\n\n\t\t/**\n\t\t * Clear all data. forces next draw to reload from the model\n\t\t */\n\t\tpublic void clearData() {\n\t\t\tclearBoxes();\n\t\t\tneedLoad = true;\n\t\t\tsetToolTipText(null);\n\t\t}\n\n\t\t/**\n\t\t * Draw the day\n\t\t * \n\t\t * @param g\n\t\t *            the Graphics to draw into\n\t\t * @param width\n\t\t *            the width\n\t\t * @param height\n\t\t *            the height\n\t\t * @param pageWidth\n\t\t *            the page width\n\t\t * @param pageHeight\n\t\t *            the page height\n\t\t * @param pagex\n\t\t *            the pagex\n\t\t * @param pagey\n\t\t *            the pagey\n\t\t * @param sm_font\n\t\t *            the font for appointment text\n\t\t * \n\t\t * @return the page exists flag for printing\n\t\t */\n\t\tprivate int drawIt(Graphics g, double width, double height,\n\t\t\t\tdouble pageWidth, double pageHeight, double pagex,\n\t\t\t\tdouble pagey, Font sm_font) {\n\n\t\t\tTheme t = Theme.getCurrentTheme();\n\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,\n\t\t\t\t\tRenderingHints.VALUE_TEXT_ANTIALIAS_ON);\n\n\t\t\t// draw a white background\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, 0, (int) width, (int) height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// translate coordinates to page margins\n\t\t\tg2.translate(pagex, pagey);\n\n\t\t\t// save original clip\n\t\t\tShape s = g2.getClip();\n\n\t\t\t// get current time\n\t\t\tGregorianCalendar now = new GregorianCalendar();\n\t\t\tint tdate = now.get(Calendar.DATE);\n\n\t\t\t// force reload if the date has changed since the last draw\n\t\t\tif (lastDrawDate != tdate) {\n\t\t\t\tneedLoad = true;\n\t\t\t}\n\t\t\tlastDrawDate = tdate;\n\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\n\t\t\t// top of drawn day - used to be non-zero to fit a title\n\t\t\tint caltop = 0;\n\n\t\t\t// height of box containing day's appts\n\t\t\tdouble rowheight = pageHeight - caltop;\n\n\t\t\t// width of column with time scale (Y axis)\n\t\t\tdouble timecolwidth = pageWidth / 15;\n\n\t\t\t// top of timed appts (the time-grid). untimed appts appear above\n\t\t\tdouble aptop = caltop + rowheight * nonTimedPortion;\n\n\t\t\t// width of each day column - related to timecolwidth\n\t\t\tdouble colwidth = (pageWidth - timecolwidth);\n\n\t\t\t// calculate the bottom and right edge of the grid\n\t\t\tint calbot = (int) rowheight + caltop;\n\n\t\t\t// limit resizing to the time-grid\n\t\t\tsetResizeBounds((int) aptop, calbot);\n\n\t\t\t// allow dragging around the entire day - across timed and untimed\n\t\t\t// areas\n\t\t\tsetDragBounds(caltop, calbot, (int) timecolwidth, (int) (pageWidth));\n\n\t\t\t// start and end hour = range of Y axis\n\t\t\tString shr = Prefs.getPref(PrefName.WKSTARTHOUR);\n\t\t\tString ehr = Prefs.getPref(PrefName.WKENDHOUR);\n\t\t\tint starthr = 7;\n\t\t\tint endhr = 22;\n\t\t\ttry {\n\t\t\t\tstarthr = Integer.parseInt(shr);\n\t\t\t\tendhr = Integer.parseInt(ehr);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t\t// calculate size of Y-axis ticks (each half-hour)\n\t\t\tint numhalfhours = (endhr - starthr) * 2;\n\t\t\tdouble tickheight = (calbot - aptop) / numhalfhours;\n\n\t\t\t// draw background of time column (where the y-axis times are shown)\n\t\t\t// using the default background color\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, caltop, (int) timecolwidth, calbot - caltop);\n\n\t\t\t// set small font for appt text\n\t\t\tg2.setFont(sm_font);\n\t\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\t\t\tif( smfontHeight == 0) smfontHeight = 7;\n\n\t\t\tint colleft = (int) (timecolwidth);\n\n\t\t\t// reload from model if needed\n\t\t\tif (needLoad) {\n\n\t\t\t\t// add the date zone for the day\n\t\t\t\taddDateZone(cal.getTime(), new Rectangle(colleft, 0,\n\t\t\t\t\t\t(int) colwidth, calbot));\n\n\t\t\t\ttry {\n\n\t\t\t\t\tstartmin = starthr * 60;\n\t\t\t\t\tendmin = endhr * 60;\n\n\t\t\t\t\t// get the day's items from the model\n\t\t\t\t\tDay dayInfo = Day.getDay(cal.get(Calendar.YEAR),\n\t\t\t\t\t\t\tcal.get(Calendar.MONTH), cal.get(Calendar.DATE));\n\n\t\t\t\t\t// set a different background color based on various\n\t\t\t\t\t// circumstances\n\t\t\t\t\tbackgroundColor = new Color(t.getDefaultBg());\n\t\t\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\n\t\t\t\t\tCalendar today = new GregorianCalendar();\n\t\t\t\t\tif (today.get(Calendar.MONTH) == month_\n\t\t\t\t\t\t\t&& today.get(Calendar.YEAR) == year_\n\t\t\t\t\t\t\t&& today.get(Calendar.DATE) == cal\n\t\t\t\t\t\t\t\t\t.get(Calendar.DATE)) {\n\t\t\t\t\t\t// day is today\n\t\t\t\t\t\tbackgroundColor = new Color(t.getTodayBg());\n\t\t\t\t\t} else if (dayInfo.getHoliday() != 0) {\n\t\t\t\t\t\t// holiday\n\t\t\t\t\t\tbackgroundColor = new Color(t.getHolidayBg());\n\t\t\t\t\t} else if (dayInfo.getVacation() == 1) {\n\t\t\t\t\t\t// full day vacation\n\t\t\t\t\t\tbackgroundColor = new Color(Theme.getCurrentTheme()\n\t\t\t\t\t\t\t\t.getVacationBg());\n\t\t\t\t\t} else if (dayInfo.getVacation() == 2) {\n\t\t\t\t\t\t// half-day vacation\n\t\t\t\t\t\tbackgroundColor = new Color(t.getHalfdayBg());\n\t\t\t\t\t} else if (dow == Calendar.SUNDAY\n\t\t\t\t\t\t\t|| dow == Calendar.SATURDAY) {\n\t\t\t\t\t\t// weekend\n\t\t\t\t\t\tbackgroundColor = new Color(t.getWeekendBg());\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// weekday\n\t\t\t\t\t\tbackgroundColor = new Color(t.getWeekdayBg());\n\t\t\t\t\t}\n\n\t\t\t\t\t// determine initial Y coord for non-scheduled appts (notes)\n\t\t\t\t\t// they will be above the timed appt area\n\t\t\t\t\tint notey = caltop;\n\n\t\t\t\t\t// loop through entities\n\t\t\t\t\tfor (CalendarEntity entity : dayInfo.getItems()) {\n\n\t\t\t\t\t\tDate d = entity.getDate();\n\n\t\t\t\t\t\t// sanity check - shouldn't happen\n\t\t\t\t\t\tif (d == null)\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t// determine appt start and end minutes\n\t\t\t\t\t\tGregorianCalendar acal = new GregorianCalendar();\n\t\t\t\t\t\tacal.setTime(d);\n\t\t\t\t\t\tdouble apstartmin = 60 * acal.get(Calendar.HOUR_OF_DAY)\n\t\t\t\t\t\t\t\t+ acal.get(Calendar.MINUTE);\n\t\t\t\t\t\tint dur = 0;\n\t\t\t\t\t\tInteger duri = entity.getDuration();\n\t\t\t\t\t\tif (duri != null) {\n\t\t\t\t\t\t\tdur = duri.intValue();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdouble apendmin = apstartmin + dur;\n\n\t\t\t\t\t\t// check if the entity is a note (untime)\n\t\t\t\t\t\t// an entity can only be timed if it is an appointment\n\t\t\t\t\t\t// that fits in the time-grid, has a duration, and is\n\t\t\t\t\t\t// timed\n\t\t\t\t\t\tif (!(entity instanceof Appointment)\n\t\t\t\t\t\t\t\t|| AppointmentModel\n\t\t\t\t\t\t\t\t\t\t.isNote((Appointment) entity)\n\t\t\t\t\t\t\t\t|| apendmin < startmin\n\t\t\t\t\t\t\t\t|| apstartmin >= endmin - 4\n\t\t\t\t\t\t\t\t|| entity.getDuration() == null\n\t\t\t\t\t\t\t\t|| entity.getDuration().intValue() == 0) {\n\n\t\t\t\t\t\t\tif (addNoteBox(\n\t\t\t\t\t\t\t\t\tcal.getTime(),\n\t\t\t\t\t\t\t\t\tentity,\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 2, notey,\n\t\t\t\t\t\t\t\t\t\t\t(int) (colwidth - 4), smfontHeight),\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, caltop,\n\t\t\t\t\t\t\t\t\t\t\t(int) colwidth,\n\t\t\t\t\t\t\t\t\t\t\t(int) (aptop - caltop))) != null) {\n\n\t\t\t\t\t\t\t\t// increment Y coord for next note text\n\t\t\t\t\t\t\t\tnotey += smfontHeight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// appt box bounds and clip are set to the whole\n\t\t\t\t\t\t\t// grid.\n\t\t\t\t\t\t\t// will be laid out later\n\t\t\t\t\t\t\taddApptBox(cal.getTime(), (Appointment) entity,\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 4, (int) aptop,\n\t\t\t\t\t\t\t\t\t\t\t(int) colwidth - 8,\n\t\t\t\t\t\t\t\t\t\t\t(int) (calbot - aptop)),\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, (int) aptop,\n\t\t\t\t\t\t\t\t\t\t\t(int) colwidth,\n\t\t\t\t\t\t\t\t\t\t\t(int) (calbot - aptop)));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t\t// layout all of the ApptBoxes\n\t\t\t\tList<ApptBox> layoutlist = new ArrayList<ApptBox>();\n\t\t\t\tfor (Box b : boxes) {\n\t\t\t\t\tif (!(b instanceof ApptBox))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tlayoutlist.add((ApptBox) b);\n\t\t\t\t}\n\t\t\t\tApptBox.layoutBoxes(layoutlist, starthr, endhr);\n\n\t\t\t}\n\n\t\t\t// draw background for day area with the user color\n\t\t\tg2.setColor(backgroundColor);\n\t\t\tg2.fillRect((int) timecolwidth, caltop,\n\t\t\t\t\t(int) (pageWidth - timecolwidth), (int) pageHeight - caltop);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// draw dashed lines for 1/2 hour intervals\n\t\t\tStroke defstroke = g2.getStroke();\n\t\t\tg2.setStroke(dashed);\n\t\t\tfor (int row = 0; row < numhalfhours; row++) {\n\t\t\t\tint rowtop = (int) ((row * tickheight) + aptop);\n\t\t\t\tg2.drawLine((int) timecolwidth, rowtop, (int) pageWidth, rowtop);\n\t\t\t}\n\t\t\tg2.setStroke(defstroke);\n\n\t\t\t// add the zoom buttons\n\t\t\tif (zoom < 4)\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\",IconHelper.getIcon(\"/resource/ZoomIn16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, caltop, 20, smfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tzoom++;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\n\t\t\tif (zoom > 0)\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/ZoomOut16.gif\"),\n\t\t\t\t\t\tnew Rectangle(colleft - 20, caltop, 20, smfontHeight),\n\t\t\t\t\t\tnull) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tzoom--;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\n\t\t\t// add the scroll buttons\n\t\t\tif (nonTimedPortion < 0.8) {\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/Down16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, (int) aptop, colleft, smfontHeight),\n\t\t\t\t\t\tnull) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tnonTimedPortion += 1.0 / 6.0;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (nonTimedPortion > 0.2) {\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/Up16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, (int) aptop - smfontHeight, colleft,\n\t\t\t\t\t\t\t\tsmfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tnonTimedPortion -= 1.0 / 6.0;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// draw all boxes\n\t\t\tg2.setClip(s);\n\t\t\tdrawBoxes(g2);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// draw the day's lines last so they show on top of other stuff\n\t\t\t// first - the horizontal lines\n\t\t\tg2.drawLine(0, caltop, (int) pageWidth, caltop);\n\t\t\tg2.drawLine(0, (int) aptop, (int) pageWidth, (int) aptop);\n\t\t\tg2.drawLine(0, calbot, (int) pageWidth, calbot);\n\t\t\tg2.drawLine(0, caltop, 0, calbot);\n\n\t\t\t// draw the Y axis time labels\n\t\t\tg2.setFont(sm_font);\n\t\t\tboolean mt = false;\n\t\t\tString mil = Prefs.getPref(PrefName.MILTIME);\n\t\t\tif (mil.equals(\"true\"))\n\t\t\t\tmt = true;\n\t\t\tfor (int row = 1; row < endhr - starthr; row++) {\n\t\t\t\tint y = (int) ((row * tickheight * 2) + aptop);\n\t\t\t\tint hr = row + starthr;\n\n\t\t\t\tif (!mt && hr > 12)\n\t\t\t\t\thr = hr - 12;\n\n\t\t\t\tString tmlabel = hr + \":00\";\n\t\t\t\tg2.drawString(tmlabel, 2, y + smfontHeight / 2);\n\t\t\t}\n\n\t\t\t// vertical lines\n\t\t\tg2.drawLine(colleft, caltop, colleft, calbot);\n\t\t\tg2.drawLine((int) pageWidth, caltop, (int) pageWidth, calbot);\n\n\t\t\tneedLoad = false;\n\n\t\t\t// for printing - in case we are drawing this for a printer\n\t\t\treturn Printable.PAGE_EXISTS;\n\t\t}\n\n\t\t/**\n\t\t * return the date for the mouse coord\n\t\t */\n\t\t@Override\n\t\tpublic Date getDateForCoord(double x, double y) {\n\t\t\t// always return the current day as this UI only shows 1 day\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\treturn cal.getTime();\n\t\t}\n\n\t\t/**\n\t\t * return the navigator label\n\t\t */\n\t\t@Override\n\t\tpublic String getNavLabel() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tDate dt = cal.getTime();\n\t\t\tDateFormat df = DateFormat.getDateInstance(DateFormat.FULL);\n\t\t\treturn df.format(dt);\n\t\t}\n\n\t\t/**\n\t\t * show the given date\n\t\t */\n\t\t@Override\n\t\tpublic void goTo(Calendar cal) {\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * react to a mouse wheel event (navigate forward or back)\n\t\t */\n\t\t@Override\n\t\tpublic void mouseWheelMoved(MouseWheelEvent e) {\n\t\t\tif (e.getWheelRotation() > 0) {\n\t\t\t\tnext();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t} else if (e.getWheelRotation() < 0) {\n\t\t\t\tprev();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * advance 1 day\n\t\t */\n\t\t@Override\n\t\tpublic void next() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tcal.add(Calendar.DATE, 1);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)\n\t\t */\n\t\t@Override\n\t\tprotected void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t\ttry {\n\t\t\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.DAYVIEWFONT));\n\t\t\t\tdrawIt(g, getWidth(), getHeight(), getWidth() - 20,\n\t\t\t\t\t\tgetHeight() - 20, 10, 10, sm_font);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw if prefs change. some prefs change the grid size\n\t\t * and/or what items get shown\n\t\t */\n\t\t@Override\n\t\tpublic void prefsChanged() {\n\t\t\tclearData();\n\t\t\trepaint();\n\n\t\t}\n\n\t\t/**\n\t\t * navigate backwards 1 day\n\t\t */\n\t\t@Override\n\t\tpublic void prev() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tcal.add(Calendar.DATE, -1);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t\t/**\n\t\t * draw the UI into a Graphics for printing\n\t\t */\n\t\t@Override\n\t\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\t\tthrows PrinterException {\n\t\t\t// only print 1 page\n\t\t\tif (pageIndex > 0)\n\t\t\t\treturn Printable.NO_SUCH_PAGE;\n\t\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.PRINTFONT));\n\t\t\tclearData();\n\t\t\tint ret = drawIt(g, pageFormat.getWidth(), pageFormat.getHeight(),\n\t\t\t\t\tpageFormat.getImageableWidth(),\n\t\t\t\t\tpageFormat.getImageableHeight(),\n\t\t\t\t\tpageFormat.getImageableX(), pageFormat.getImageableY(),\n\t\t\t\t\tsm_font);\n\t\t\trefresh();\n\t\t\treturn ret;\n\t\t}\n\n\t\t/**\n\t\t * reload data and redraw\n\t\t */\n\t\t@Override\n\t\tpublic void refresh() {\n\t\t\tToolkit toolkit = Toolkit.getDefaultToolkit();\n\t\t\tDimension dim = toolkit.getScreenSize();\n\t\t\tint h = ((zoom + 2) * (int) dim.getHeight()) / 2;\n\t\t\tif (zoom == 0)\n\t\t\t\th = 0;\n\t\t\tthis.setPreferredSize(new Dimension(0, h));\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tthis.getParent().doLayout();\n\t\t}\n\n\t\t@Override\n\t\tpublic void update(ChangeEvent event) {\n\t\t\trefresh();\n\t\t}\n\n\t\t/**\n\t\t * navigate to the current date\n\t\t */\n\t\t@Override\n\t\tpublic void today() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t}\n\n\t// container panel for the day boxes\n\tprivate DaySubPanel dp_ = null;\n\n\t// navigation panel\n\tprivate NavPanel nav = null;\n\t\n\tprivate boolean isInitialized = false;\n\n\n\t/**\n\t * Instantiates a new day panel.\n\t * \n\t */\n\tpublic DayPanel() {\n\n\t\t\n\t}\n\n\t/**\n\t * Go to a particular date\n\t * \n\t * @param cal\n\t *            the date\n\t */\n\t@Override\n\tpublic void goTo(Calendar cal) {\n\t\tif( isInitialized ){\n\t\t\tdp_.goTo(cal);\n\t\t\tnav.setLabel(dp_.getNavLabel());\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.print.Printable#print(java.awt.Graphics,\n\t * java.awt.print.PageFormat, int)\n\t */\n\t@Override\n\tpublic int print(Graphics arg0, PageFormat arg1, int arg2)\n\t\t\tthrows PrinterException {\n\t\treturn dp_.print(arg0, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Day_View\");\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\t\t\n\t\tif( !isInitialized )\n\t\t{\n\t\t\t// create the day ui and attached navigator\n\t\t\tdp_ = new DaySubPanel();\n\t\t\tnav = new NavPanel(dp_);\n\n\t\t\tJScrollPane sp = new JScrollPane();\n\t\t\tsp.setViewportView(dp_);\n\n\t\t\tsetLayout(new java.awt.GridBagLayout());\n\t\t\tadd(nav,\n\t\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\t\t\tadd(sp, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH,\n\t\t\t\t\t1.0, 1.0));\n\t\t\tisInitialized = true;\n\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/day.jpg\"),\n\t\t\t\tgetModuleName(), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(ViewType.DAY);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.DAY);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tPrintHelper.printPrintable(this);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.DAY;\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\t// do nothing - children do their own refresh\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// do nothing - children do their own refresh\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/LabelBox.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.LabelEntity;\n\nimport java.awt.*;\n\n/**\n * A Box that shows a text label on the calendar. It cannot be edited, moved, or\n * deleted. It is associated with a label entity, such as a hard-coded holiday\n * or birthday\n */\nclass LabelBox extends Box {\n\n\t/**\n\t * the model entity that the LabelBox is showing\n\t */\n\tprivate LabelEntity entity = null;\n\n\t/**\n\t * constructor\n\t * \n\t * @param ap     calendar entity associated with this box\n\t * @param bounds bounds\n\t * @param clip   clip\n\t */\n\tpublic LabelBox(LabelEntity ap, Rectangle bounds, Rectangle clip) {\n\t\tsuper(bounds, clip);\n\t\tentity = ap;\n\t}\n\n\t@Override\n\tpublic void draw(Graphics2D g2, Component comp) {\n\n\t\tShape s = g2.getClip();\n\t\tif (clip != null)\n\t\t\tg2.setClip(clip);\n\n\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\t\tTheme t = Theme.getCurrentTheme();\n\n\t\tint cornerRadius = bounds.height - 2; // Creates pill-shaped ends\n\n\t\t// Draw colored pill background\n\t\tif (t.isPills()) {\n\t\t\tg2.setColor(new Color(t.colorFromString(getTextColor())));\n\t\t\tg2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));\n\t\t\tg2.fillRoundRect(bounds.x, bounds.y + 4, bounds.width, bounds.height, cornerRadius, cornerRadius);\n\t\t\tg2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));\n\t\t}\n\n\t\tif (isSelected == true) {\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\t\t\tg2.fillRoundRect(bounds.x, bounds.y + 4, bounds.width, bounds.height, cornerRadius, cornerRadius);\n\t\t}\n\n\t\tif (isSelected == true)\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\telse\n\t\t\tg2.setColor(new Color(t.colorFromString(getTextColor())));\n\n\t\tg2.drawString(getText(), bounds.x + 2, bounds.y + smfontHeight);\n\t\tg2.setColor(Color.black);\n\n\t\tg2.setClip(s);\n\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn entity.getText();\n\t}\n\n\t/**\n\t * get the text color of the associated entity\n\t * \n\t * @return the text color or null\n\t */\n\tprivate String getTextColor() {\n\t\tif (entity == null)\n\t\t\treturn null;\n\n\t\treturn entity.getColor();\n\t}\n\n\t@Override\n\tpublic void onClick() {\n\t\t// empty\n\t}\n\n\t@Override\n\tpublic String getToolTipText() {\n\t\treturn entity.getTooltipText();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/MonthPanel.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GridBagConstraints;\nimport java.awt.Rectangle;\nimport java.awt.RenderingHints;\nimport java.awt.Shape;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseWheelEvent;\nimport java.awt.event.MouseWheelListener;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\nimport javax.swing.Icon;\nimport javax.swing.JPanel;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Day;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.CalendarModule;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.NavPanel;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * MonthPanel is the UI for a calendar month. It is Printable, but is NOT used\n * for printing a monthly calendar as borg has a more specialized month printing\n * class.\n */\npublic class MonthPanel extends JPanel implements Printable, CalendarModule {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * MonthViewSubPanel draws a month and provides the UI container for the\n\t * month items\n\t */\n\tprivate class MonthViewSubPanel extends ApptBoxPanel implements Printable,\n\t\t\tNavPanel.Navigator, Model.Listener, Prefs.Listener,\n\t\t\tMouseWheelListener {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t// number of boxes on the calendar - 6 rows of 7 days\n\t\tstatic final private int numBoxes = 42;\n\n\t\t// width of a column of days\n\t\tprivate int colwidth;\n\n\t\t// colors of the days\n\t\tColor[] colors = new Color[numBoxes];\n\n\t\t// top of a day - including the day label\n\t\tprivate int daytop;\n\n\t\t// records the last date on which a draw took place - for handling the\n\t\t// first redraw after midnight\n\t\tprivate int lastDrawDate = -1;\n\n\t\t// the month being displayed\n\t\tprivate int month_;\n\t\tprivate int year_;\n\n\t\t// flag to indicate if we need to reload from the model. If false, we\n\t\t// can just redraw\n\t\t// from cached data\n\t\tboolean needLoad = true;\n\n\t\t// height of a row of days\n\t\tprivate int rowheight;\n\n\t\t/**\n\t\t * constructor\n\t\t */\n\t\tpublic MonthViewSubPanel() {\n\n\t\t\t// react to pref changes\n\t\t\tPrefs.addListener(this);\n\n\t\t\t// react to mouse wheel events\n\t\t\taddMouseWheelListener(this);\n\n\t\t\t// react to task or appt changes\n\t\t\tAppointmentModel.getReference().addListener(this);\n\t\t\tTaskModel.getReference().addListener(this);\n\t\t\tAddressModel.getReference().addListener(this);\n\t\t\tSubscribedCalendars.getReference().addListener(this);\n\t\t\t\n\t\t\tgoTo(new GregorianCalendar());\n\n\t\t}\n\n\t\t/**\n\t\t * clear cached data and force reload on next draw\n\t\t */\n\t\tpublic void clearData() {\n\t\t\tclearBoxes();\n\t\t\tneedLoad = true;\n\t\t\tsetToolTipText(null);\n\t\t}\n\n\t\t/**\n\t\t * draw month into a Graphics\n\t\t */\n\t\tprivate int drawIt(Graphics g, double width, double height,\n\t\t\t\tdouble pageWidth, double pageHeight, double pagex,\n\t\t\t\tdouble pagey) {\n\n\t\t\tTheme t = Theme.getCurrentTheme();\n\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,  \n\t                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);  \n\n\t\t\t// appt text font\n\t\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.APPTFONT));\n\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, 0, (int) width, (int) height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// get font sizes\n\t\t\tint fontHeight = g2.getFontMetrics().getHeight();\n\t\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\n\t\t\t// translate coordinates based on the page margins\n\t\t\tg2.translate(pagex, pagey);\n\n\t\t\tShape s = g2.getClip();\n\n\t\t\t// get current time\n\t\t\tGregorianCalendar now = new GregorianCalendar();\n\t\t\tint tmon = now.get(Calendar.MONTH);\n\t\t\tint tyear = now.get(Calendar.YEAR);\n\t\t\tint tdate = now.get(Calendar.DATE);\n\n\t\t\t// force reload if the date has changed since the last draw\n\t\t\tif (lastDrawDate != tdate) {\n\t\t\t\tneedLoad = true;\n\t\t\t}\n\t\t\tlastDrawDate = tdate;\n\n\t\t\t// get first day of the month and set first day of week based on\n\t\t\t// user pref\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, 1);\n\t\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\n\t\t\t// top of drawn month - used to be non-zero to fit a title\n\t\t\tint caltop = 0;\n\n\t\t\t// set top of the day - which is under the weekday name\n\t\t\tdaytop = caltop + fontHeight + fontDesent;\n\n\t\t\t// set width of the week button on the right edge of each week\n\t\t\tint weekbutwidth = fontHeight + fontDesent;\n\n\t\t\t// calculate width and height of day boxes (6x7 grid)\n\t\t\trowheight = ((int) pageHeight - daytop) / 6;\n\t\t\tcolwidth = (int) (pageWidth - weekbutwidth) / 7;\n\n\t\t\t// calculate the bottom and right edge of the grid\n\t\t\tint calbot = 6 * rowheight + daytop;\n\t\t\tint calright = 7 * colwidth;\n\n\t\t\t// allow items to be dragged all over the calendar (but not over the\n\t\t\t// weekday labels or week buttons\n\t\t\tsetDragBounds(daytop, calbot, 0, (int) pageWidth - weekbutwidth);\n\n\t\t\t// do not allow any resizing or dragging out of new appointments\n\t\t\t// since\n\t\t\t// the month UI does not have a time-grid\n\t\t\tsetResizeBounds(0, 0);\n\n\t\t\t// draw background for weekday labels default color\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, caltop, calright, daytop - caltop);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// draw the weekday names centered in each column - no boxes drawn\n\t\t\t// yet\n\t\t\tSimpleDateFormat dfw = new SimpleDateFormat(\"EEE\");\n\t\t\tcal.add(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek()\n\t\t\t\t\t- cal.get(Calendar.DAY_OF_WEEK));\n\t\t\tfor (int col = 0; col < 7; col++) {\n\t\t\t\tint colleft = (col * colwidth);\n\t\t\t\tString dayofweek = dfw.format(cal.getTime());\n\t\t\t\tint swidth = g2.getFontMetrics().stringWidth(dayofweek);\n\t\t\t\tg2.drawString(dayofweek, colleft + (colwidth - swidth) / 2,\n\t\t\t\t\t\tcaltop + fontHeight);\n\t\t\t\tcal.add(Calendar.DAY_OF_WEEK, 1);\n\t\t\t}\n\n\t\t\t// reset time to first day on calendar\n\t\t\t// *** this will likely be a day from the prior month\n\t\t\t// we do show prior month and next month days in any of the 42 boxes\n\t\t\t// not used for the current month\n\t\t\tcal.set(year_, month_, 1);\n\t\t\tint fdow = cal.get(Calendar.DAY_OF_WEEK) - cal.getFirstDayOfWeek();\n\t\t\tif (fdow == -1)\n\t\t\t\tfdow = 6;\n\t\t\tcal.add(Calendar.DATE, -1 * fdow);\n\n\t\t\t// draw the days - all 42!!\n\t\t\tfor (int box = 0; box < numBoxes; box++) {\n\n\t\t\t\t// calculate column and row\n\t\t\t\tint boxcol = box % 7;\n\t\t\t\tint boxrow = box / 7;\n\n\t\t\t\t// calculate top left of the day area\n\t\t\t\tint rowtop = (boxrow * rowheight) + daytop;\n\t\t\t\tint colleft = boxcol * colwidth;\n\n\t\t\t\t// get day of week\n\t\t\t\tint dow = cal.getFirstDayOfWeek() + boxcol;\n\t\t\t\tif (dow == 8)\n\t\t\t\t\tdow = 1;\n\n\t\t\t\t// set small font for appt text\n\t\t\t\tg2.setFont(sm_font);\n\t\t\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\n\t\t\t\t// set clip to the day box to truncate long appointment text\n\t\t\t\tg2.clipRect(colleft, rowtop, colwidth, rowheight);\n\n\t\t\t\t// check if we need to reload form the model\n\t\t\t\tif (needLoad) {\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\t// add adate zone to this box for the date\n\t\t\t\t\t\taddDateZone(cal.getTime(), new Rectangle(colleft,\n\t\t\t\t\t\t\t\trowtop, colwidth, rowheight));\n\n\t\t\t\t\t\t// get the appointment info for the given day\n\t\t\t\t\t\tDay dayInfo = Day.getDay(cal.get(Calendar.YEAR), cal\n\t\t\t\t\t\t\t\t.get(Calendar.MONTH), cal.get(Calendar.DATE));\n\n\t\t\t\t\t\t// set a different background color based on various\n\t\t\t\t\t\t// circumstances\n\t\t\t\t\t\tColor c = null;\n\t\t\t\t\t\tif (tmon == month_ && tyear == year_\n\t\t\t\t\t\t\t\t&& tdate == cal.get(Calendar.DATE)) {\n\t\t\t\t\t\t\t// day is today\n\t\t\t\t\t\t\tc = new Color(t.getTodayBg());\n\t\t\t\t\t\t} else if (dayInfo.getHoliday() != 0) {\n\t\t\t\t\t\t\t// holiday\n\t\t\t\t\t\t\tc = new Color(t.getHolidayBg());\n\t\t\t\t\t\t} else if (dayInfo.getVacation() == 1) {\n\t\t\t\t\t\t\t// full day vacation\n\t\t\t\t\t\t\tc = new Color(Theme.getCurrentTheme().getVacationBg());\n\t\t\t\t\t\t} else if (dayInfo.getVacation() == 2) {\n\t\t\t\t\t\t\t// half-day vacation\n\t\t\t\t\t\t\tc = new Color(t.getHalfdayBg());\n\t\t\t\t\t\t} else if (dow == Calendar.SUNDAY\n\t\t\t\t\t\t\t\t|| dow == Calendar.SATURDAY) {\n\t\t\t\t\t\t\t// weekend\n\t\t\t\t\t\t\tc = new Color(t.getWeekendBg());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// weekday\n\t\t\t\t\t\t\tc = new Color(t.getWeekdayBg());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// if a day is not in the current month, then always use\n\t\t\t\t\t\t// the default background\n\t\t\t\t\t\tif (cal.get(Calendar.MONTH) != month_)\n\t\t\t\t\t\t\tc = new Color(t.getDefaultBg());\n\n\t\t\t\t\t\t// remember the color for when we redraw from cache\n\t\t\t\t\t\tcolors[box] = c;\n\n\t\t\t\t\t\t// set initial Y coord for item text\n\t\t\t\t\t\tint notey = rowtop + smfontHeight;\n\n\t\t\t\t\t\t// loop through entities for the day\n\t\t\t\t\t\tfor (CalendarEntity entity : dayInfo.getItems()) {\n\n\t\t\t\t\t\t\t// add the item NoteBox to the container\n\t\t\t\t\t\t\tif (addNoteBox(cal.getTime(), entity,\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 2, notey,\n\t\t\t\t\t\t\t\t\t\t\tcolwidth - 4, smfontHeight),\n\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, rowtop, colwidth,\n\t\t\t\t\t\t\t\t\t\t\trowheight)) != null)\n\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// increment Y coord for next note text\n\t\t\t\t\t\t\t\tnotey += smfontHeight;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// check if we clipped some appts - meaning that they do\n\t\t\t\t\t\t// not all fit vertically\n\t\t\t\t\t\t// in the day's box due to lack of room\n\t\t\t\t\t\tIcon clipIcon = null;\n\t\t\t\t\t\tif (notey > rowtop + rowheight) {\n\t\t\t\t\t\t\t// set clipping indication icon so that we dray it\n\t\t\t\t\t\t\t// next to the date label\n\t\t\t\t\t\t\tclipIcon = IconHelper.getIcon(\n\t\t\t\t\t\t\t\t\t\"/resource/Import16.gif\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// add a label for the date. this is actually a button\n\t\t\t\t\t\t// box and the user\n\t\t\t\t\t\t// can press it to go to that date's day view\n\t\t\t\t\t\tString datetext = Integer.toString(cal\n\t\t\t\t\t\t\t\t.get(Calendar.DATE));\n\t\t\t\t\t\tif (Prefs.getPref(PrefName.DAYOFYEAR).equals(\"true\"))\n\t\t\t\t\t\t\tdatetext += \"   [\" + cal.get(Calendar.DAY_OF_YEAR)\n\t\t\t\t\t\t\t\t\t+ \"]\";\n\t\t\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), datetext,\n\t\t\t\t\t\t\t\tclipIcon, new Rectangle(colleft + 2, rowtop,\n\t\t\t\t\t\t\t\t\t\tcolwidth - 4, smfontHeight),\n\t\t\t\t\t\t\t\tnew Rectangle(colleft, rowtop, colwidth,\n\t\t\t\t\t\t\t\t\t\tsmfontHeight + 2), c) {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\t\t\tMultiView.getMainView().setView(ViewType.DAY);\n\t\t\t\t\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\t\t\t\t\tgc.setTime(getDate());\n\t\t\t\t\t\t\t\tMultiView.getMainView().goTo(gc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// reset the clip or bad things happen\n\t\t\t\tg2.setClip(s);\n\n\t\t\t\t// fill box with background color\n\t\t\t\tg2.setColor(colors[box]);\n\t\t\t\tg2.fillRect(colleft, rowtop, colwidth, rowheight);\n\n\t\t\t\t// increment the day\n\t\t\t\tcal.add(Calendar.DATE, 1);\n\t\t\t}\n\n\t\t\t// add week buttons along the right side. they display the week\n\t\t\t// number\n\t\t\t// and will bring up the week view if pressed\n\t\t\tif (needLoad) {\n\t\t\t\t// use iso week numbering if option set\n\t\t\t\tif (Prefs.getPref(PrefName.ISOWKNUMBER).equals(\"true\"))\n\t\t\t\t\tcal.setMinimalDaysInFirstWeek(4);\n\t\t\t\telse\n\t\t\t\t\tcal.setMinimalDaysInFirstWeek(1);\n\t\t\t\tfor (int row = 0; row < 6; row++) {\n\n\t\t\t\t\tcal.set(year_, month_, 1 + 7 * row);\n\t\t\t\t\tint wk = cal.get(Calendar.WEEK_OF_YEAR);\n\t\t\t\t\tint rowtop = (row * rowheight) + daytop;\n\t\t\t\t\tboxes.add(new ButtonBox(cal.getTime(),\n\t\t\t\t\t\t\tInteger.toString(wk), null, new Rectangle(\n\t\t\t\t\t\t\t\t\t(int) pageWidth - weekbutwidth, rowtop,\n\t\t\t\t\t\t\t\t\tweekbutwidth, rowheight), new Rectangle(\n\t\t\t\t\t\t\t\t\t(int) pageWidth - weekbutwidth, rowtop,\n\t\t\t\t\t\t\t\t\tweekbutwidth+1, rowheight+1)) {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\t\tMultiView.getMainView().setView(ViewType.WEEK);\n\t\t\t\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\t\t\t\tgc.setTime(getDate());\n\t\t\t\t\t\t\tMultiView.getMainView().goTo(gc);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tneedLoad = false;\n\t\t\t\n\n\t\t\t// draw all items\n\t\t\tdrawBoxes(g2);\n\t\t\tg2.setClip(s);\n\n\t\t\t// draw the lines last\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// horizontal line at top of calendar - above day names\n\t\t\tg2.drawLine(0, caltop, calright, caltop);\n\n\t\t\t// horizontal lines for each row from below day names to bottom\n\t\t\tfor (int row = 0; row < 7; row++) {\n\t\t\t\tint rowtop = (row * rowheight) + daytop;\n\t\t\t\tg2.drawLine(0, rowtop, calright, rowtop);\n\t\t\t}\n\n\t\t\t// vertcal lines\n\t\t\tfor (int col = 0; col < 8; col++) {\n\t\t\t\tint colleft = (col * colwidth);\n\t\t\t\tg2.drawLine(colleft, caltop, colleft, calbot);\n\t\t\t}\n\n\t\t\treturn Printable.PAGE_EXISTS;\n\t\t}\n\n\t\t/**\n\t\t * return the date corresponding to the box that the given x/y\n\t\t * coordinate is in\n\t\t */\n\t\t@Override\n\t\tpublic Date getDateForCoord(double x, double y) {\n\n\t\t\tint col = (int) x / colwidth;\n\t\t\tif( col > 6 ) col = 6; // max is 6 cols\n\t\t\tint row = ((int) y - daytop) / rowheight;\n\t\t\tif( row > 5) row = 5; // max is 5 rows on month view\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, 1);\n\t\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\t\t\tint fdow = cal.get(Calendar.DAY_OF_WEEK) - cal.getFirstDayOfWeek();\n\t\t\tif( fdow < 0 ) fdow += 7; // adjustment for fdow = MON and month starts on SUN\n\t\t\tcal.add(Calendar.DATE, -1 * fdow);\n\t\t\tcal.add(Calendar.DATE, row * 7 + col);\n\t\t\treturn cal.getTime();\n\t\t}\n\n\t\t/**\n\t\t * get the navigator label\n\t\t */\n\t\t@Override\n\t\tpublic String getNavLabel() {\n\t\t\tSimpleDateFormat df = new SimpleDateFormat(\"MMMM yyyy\");\n\t\t\tCalendar cal = new GregorianCalendar(year_, month_, 1);\n\t\t\treturn df.format(cal.getTime());\n\t\t}\n\n\t\t/**\n\t\t * go to a particular month\n\t\t */\n\t\t@Override\n\t\tpublic void goTo(Calendar cal) {\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * navigate forward or backward 1 month when mouse wheel moved\n\t\t */\n\t\t@Override\n\t\tpublic void mouseWheelMoved(MouseWheelEvent e) {\n\t\t\tif (e.getWheelRotation() > 0) {\n\t\t\t\tnext();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t} else if (e.getWheelRotation() < 0) {\n\t\t\t\tprev();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * navigate forward 1 month\n\t\t */\n\t\t@Override\n\t\tpublic void next() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, 1, 23,\n\t\t\t\t\t59);\n\t\t\tcal.add(Calendar.MONTH, 1);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\t\t}\n\n\t\t@Override\n\t\tprotected void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t\ttry {\n\n\t\t\t\tdrawIt(g, getWidth(), getHeight(), getWidth() - 20,\n\t\t\t\t\t\tgetHeight() - 20, 10, 10);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Errmsg.getErrorHandler().errmsg(e);\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw if prefs changed\n\t\t */\n\t\t@Override\n\t\tpublic void prefsChanged() {\n\t\t\tclearData();\n\t\t\trepaint();\n\n\t\t}\n\n\t\t/**\n\t\t * navigate backwards 1 month\n\t\t */\n\t\t@Override\n\t\tpublic void prev() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, 1, 23,\n\t\t\t\t\t59);\n\t\t\tcal.add(Calendar.MONTH, -1);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t\t/**\n\t\t * draw to Graphics for printing\n\t\t */\n\t\t@Override\n\t\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\t\tthrows PrinterException {\n\n\t\t\tif (pageIndex > 1)\n\t\t\t\treturn Printable.NO_SUCH_PAGE;\n\n\t\t\treturn (drawIt(g, pageFormat.getWidth(), pageFormat.getHeight(),\n\t\t\t\t\tpageFormat.getImageableWidth(), pageFormat\n\t\t\t\t\t\t\t.getImageableHeight(), pageFormat.getImageableX(),\n\t\t\t\t\tpageFormat.getImageableY()));\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw\n\t\t */\n\t\t@Override\n\t\tpublic void refresh() {\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic void update(ChangeEvent event) {\n\t\t\trefresh();\n\t\t}\n\n\t\t/**\n\t\t * go to current month\n\t\t */\n\t\t@Override\n\t\tpublic void today() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t}\n\n\t// navigator panel\n\tprivate NavPanel nav = null;\n\n\t// month UI panel\n\tprivate MonthViewSubPanel monthSubPanel = null;\n\t\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t */\n\tpublic MonthPanel() {\n\n\t\t\n\t}\n\n\t/**\n\t * go to a particular month\n\t */\n\t@Override\n\tpublic void goTo(Calendar cal) {\n\t\tif( isInitialized ){\n\t\t\tmonthSubPanel.goTo(cal);\n\t\t\tnav.setLabel(monthSubPanel.getNavLabel());\n\t\t}\n\t}\n\n\t/**\n\t * print month to a Graphics\n\t */\n\t@Override\n\tpublic int print(Graphics arg0, PageFormat arg1, int arg2)\n\t\t\tthrows PrinterException {\n\t\treturn monthSubPanel.print(arg0, arg1, arg2);\n\t}\n\n\t/**\n\t * prints 1 or more months when the user requests a month print. This method\n\t * currently redirects to the MonthPrintPanel object to do the actual\n\t * drawing as the borg month printout is specialized for a paper printout\n\t * and does not use the MonthPanel view for printing\n\t */\n\tpublic void printMonths() {\n\t\ttry {\n\t\t\tMonthPrintPanel.printMonths(monthSubPanel.month_,\n\t\t\t\t\tmonthSubPanel.year_);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(IconHelper.getIcon(\n\t\t\"/resource/month.gif\"), getModuleName(), \n\t\tnew ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.MONTH);\n\t\t\t}\n\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), \n\t\tnew ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.MONTH);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\t\t\n\t\tif( !isInitialized)\n\t\t{\n\t\t\t// create the month UI panel\n\t\t\tmonthSubPanel = new MonthViewSubPanel();\n\n\t\t\t// create the navigator panel\n\t\t\tnav = new NavPanel(monthSubPanel);\n\n\t\t\tsetLayout(new java.awt.GridBagLayout());\n\t\t\tadd(nav, GridBagConstraintsFactory\n\t\t\t\t\t.create(0, 0, GridBagConstraints.BOTH));\n\t\t\tadd(monthSubPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Month_View\");\n\t}\n\t\n\t@Override\n\tpublic void print() {\n\t\tprintMonths();\n\t}\n\t\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.MONTH;\n\t}\n\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/MonthPrintPanel.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport net.sf.borg.common.*;\nimport net.sf.borg.model.Day;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.font.TextAttribute;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\nimport java.io.Serializable;\nimport java.text.AttributedString;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\n/**\n * MonthPrintPanel creates the Borg month printouts\n */\npublic class MonthPrintPanel extends JPanel implements Printable {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t// scale factor used when this class draws a month printout on the UI\n\t// not currently used\n\tstatic private final double prev_scale = 1.5;\n\n\t/**\n\t * prompt the user for a number of months to print (1-12), starting with the currently displayed one,\n\t * and then print the months\n\t * @param month displayed month\n\t * @param year displayed year\n\t * @throws Exception\n\t */\n\tpublic static void printMonths(int month, int year) throws Exception {\n\n\t\t// use the Java print service\n\t\t// this relies on monthPanel.print to fill in a Graphic object and\n\t\t// respond to the Printable API\n\t\tMonthPrintPanel cp = new MonthPrintPanel(month, year);\n\t\tObject[] options = { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3),\n\t\t\t\tInteger.valueOf(4), Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7),\n\t\t\t\tInteger.valueOf(8), Integer.valueOf(9), Integer.valueOf(10),\n\t\t\t\tInteger.valueOf(11), Integer.valueOf(12) };\n\t\tObject choice = JOptionPane.showInputDialog(null, Resource\n\t\t\t\t.getResourceString(\"nummonths\"), Resource\n\t\t\t\t.getResourceString(\"Print_Chooser\"),\n\t\t\t\tJOptionPane.QUESTION_MESSAGE, null, options, options[0]);\n\t\tif (choice == null)\n\t\t\treturn;\n\t\t\n\t\t// print the months\n\t\tInteger i = (Integer) choice;\n\t\tcp.setPages(i.intValue());\n\t\tPrintHelper.printPrintable(cp);\n\t\t//JFrame f = new JFrame();\n\t\t//f.setContentPane(cp);\n\t\t//f.setVisible(true);\n\n\t}\n\n\t// start month\n\tprivate final int month_;\n\n\t// number of pages to print\n\tprivate int pages_ = 1;\n\n\t// start year\n\tprivate final int year_;\n\n\t/**\n\t * constructor\n\t * @param month start month\n\t * @param year start year\n\t */\n\tpublic MonthPrintPanel(int month, int year) {\n\t\tyear_ = year;\n\t\tmonth_ = month;\n\t}\n\n\t/**\n\t * draw the month printout to a Graphics\n\t */\n\tprivate int drawIt(Graphics g, double width, double height,\n\t\t\tdouble pageWidth, double pageHeight, double pagex, double pagey,\n\t\t\tint pageIndex) {\n\t\t\n\t\tGraphics2D g2 = (Graphics2D) g;\n\n\t\tint year;\n\t\tint month;\n\n\t\t// see if color printout option set\n\t\tString cp = \"false\";\n\t\ttry {\n\t\t\tcp = Prefs.getPref(PrefName.COLORPRINT);\n\t\t} catch (Exception e) {\n\t\t  // empty\n\t\t}\n\n\t\t// get fonts\n\t\tFont def_font = g2.getFont();\n\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.PRINTFONT));\n\t\tMap<TextAttribute, Serializable> stmap = new HashMap<TextAttribute, Serializable>();\n\t\tstmap.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);\n\t\tstmap.put(TextAttribute.FONT, sm_font);\n\n\t\t// draw a white background\n\t\tg2.setColor(Color.white);\n\t\tg2.fillRect(0, 0, (int) width, (int) height);\n\t\tg2.setColor(Color.black);\n\n\t\t// get font sizes\n\t\tint fontHeight = g2.getFontMetrics().getHeight();\n\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\t\t\n\t\t// bug fix if font metrics missing\n\t\tif( fontHeight == 0) fontHeight = 16;\n\t\tif( fontDesent == 0) fontDesent = 3;\n\n\t\t// translate coordinates based on the page margins\n\t\tg2.translate(pagex, pagey);\n\t\tShape s = g2.getClip();\n\n\t\t// determine month based on the page we are on (pageIndex)\n\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, 1);\n\t\tcal.add(Calendar.MONTH, pageIndex);\n\t\tyear = cal.get(Calendar.YEAR);\n\t\tmonth = cal.get(Calendar.MONTH);\n\t\t\n\t\t// set first day of week\n\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\n\t\t// month title\n\t\tDate then = cal.getTime();\n\t\tSimpleDateFormat sd = new SimpleDateFormat(\"MMMM yyyy\");\n\t\tString title = sd.format(then);\n\n\t\t// draw of title at correct height and centered\n\t\t// horizontally on page\n\t\tint titlewidth = g2.getFontMetrics().stringWidth(title);\n\t\tint caltop = fontHeight + fontDesent;\n\t\tint daytop = caltop + fontHeight + fontDesent;\n\t\tg2.drawString(title, ((int) pageWidth - titlewidth) / 2, fontHeight);\n\n\t\t// calculate width and height of day boxes (6x7 grid)\n\t\tint rowheight = ((int) pageHeight - daytop) / 6;\n\t\tint colwidth = (int) pageWidth / 7;\n\n\t\t// calculate the bottom and right edge of the grid\n\t\tint calbot = 6 * rowheight + daytop;\n\t\tint calright = 7 * colwidth;\n\n\t\t// draw the day of week names centered in each column - no boxes drawn yet\n\t\tSimpleDateFormat dfw = new SimpleDateFormat(\"EEE\");\n\t\tcal.add(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek()\n\t\t\t\t- cal.get(Calendar.DAY_OF_WEEK));\t\n\t\tfor (int col = 0; col < 7; col++) {\n\t\t\tint colleft = (col * colwidth);\n\t\t\tString dayofweek = dfw.format(cal.getTime());\n\t\t\tint swidth = g2.getFontMetrics().stringWidth(dayofweek);\n\t\t\tg2.drawString(dayofweek, colleft + (colwidth - swidth) / 2, caltop\n\t\t\t\t\t+ fontHeight);\n\t\t\tcal.add(Calendar.DAY_OF_WEEK, 1);\n\t\t}\n\n\t\t// reset date\n\t\tcal.set(year, month, 1);\n\t\tint fdow = cal.get(Calendar.DAY_OF_WEEK) - cal.getFirstDayOfWeek();\n\t\tif( fdow < 0 ) fdow = fdow + 7;\n\n\t\t// print the days - either grayed out or containing a number and appts\n\t\tfor (int box = 0; box < 42; box++) {\n\n\t\t\t// month length\n\t\t\tint mlen = cal.getActualMaximum(Calendar.DAY_OF_MONTH);\n\n\t\t\t// determine row and column\n\t\t\tint boxcol = box % 7;\n\t\t\tint boxrow = box / 7;\n\t\t\t\n\t\t\t// determine top left of the current day box\n\t\t\tint rowtop = (boxrow * rowheight) + daytop;\n\t\t\tint colleft = boxcol * colwidth;\n\t\t\t\n\t\t\t// get day of week\n\t\t\tint dow = cal.getFirstDayOfWeek() + boxcol;\n\t\t\tif (dow == 8)\n\t\t\t\tdow = 1;\n\n\t\t\t// if box in grid is before first day or after last of the month, just draw a\n\t\t\t// gray box\n\t\t\tif (box < fdow || box > fdow + mlen - 1) {\n\t\t\t\t// gray\n\t\t\t\tif (cp.equals(\"false\")) {\n\t\t\t\t\t// onyl draw gray box if color print option is not on\n\t\t\t\t\tg2.setColor(new Color(235, 235, 235));\n\t\t\t\t\tg2.fillRect(colleft, rowtop, colwidth, rowheight);\n\t\t\t\t\tg2.setColor(Color.black);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t// get date\n\t\t\t\tint date = box - fdow + 1;\n\n\t\t\t\t// set small font for appt text\n\t\t\t\tg2.setFont(sm_font);\n\t\t\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\t\t\t\tif( smfontHeight == 0) smfontHeight = 7;\n\n\t\t\t\t// set clip to the day box to truncate long appointment text\n\t\t\t\tg2.clipRect(colleft, rowtop, colwidth, rowheight);\n\t\t\t\t\n\t\t\t\ttry {\n\n\t\t\t\t\t// get the appointment info for the given day\n\t\t\t\t\tGregorianCalendar gc = new GregorianCalendar(year, month,\n\t\t\t\t\t\t\tdate);\n\t\t\t\t\tDay dayInfo = Day.getDay(year, month, date);\n\t\t\t\t\tif (dayInfo != null) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// vary the background color for color printing\n\t\t\t\t\t\t// the user colors are not used. colors that don't waste ink are used\n\t\t\t\t\t\tif (cp.equals(\"true\")) {\n\t\t\t\t\t\t\tif (dayInfo.getVacation() != 0) {\n\t\t\t\t\t\t\t\tg2.setColor(new Color(225, 255, 225));\n\t\t\t\t\t\t\t} else if (dayInfo.getHoliday() == 1) {\n\t\t\t\t\t\t\t\tg2.setColor(new Color(255, 225, 195));\n\t\t\t\t\t\t\t} else if (dow == Calendar.SUNDAY\n\t\t\t\t\t\t\t\t\t|| dow == Calendar.SATURDAY) {\n\t\t\t\t\t\t\t\tg2.setColor(new Color(255, 225, 195));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tg2.setColor(new Color(255, 245, 225));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tg2.fillRect(colleft, rowtop, colwidth, rowheight);\n\t\t\t\t\t\t\tg2.setColor(Color.black);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tTheme t = Theme.getCurrentTheme();\n\t\t\t\t\t\tCollection<CalendarEntity> appts = dayInfo.getItems();\n\t\t\t\t\t\tif (appts != null) {\n\n\t\t\t\t\t\t\t// determine X,Y coords of first appt text\n\t\t\t\t\t\t\tint apptx = colleft + 2 * fontDesent;\n\t\t\t\t\t\t\tint appty = rowtop + fontHeight + smfontHeight;\n\n\t\t\t\t\t\t\tfor( CalendarEntity entity : appts ) {\n\n\t\t\t\t\t\t\t\t// change color for a single appointment based\n\t\t\t\t\t\t\t\t// on its color - only if color print option set\n\t\t\t\t\t\t\t\t// *** this uses the original borg colors - not the user colors\n\t\t\t\t\t\t\t\t// should be changed some time\n\t\t\t\t\t\t\t\tif (cp.equals(\"false\"))\n\t\t\t\t\t\t\t\t\tg2.setColor(Color.black);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tg2.setColor(new Color(t.colorFromString(entity.getColor())));\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tString text = entity.getText();\n\t\t\t\t\t\t\t\tif( entity instanceof Appointment)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttext = AppointmentTextFormat.format((Appointment)entity, gc.getTime());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t// skip items that are strike-through if the option to skip them is set\n\t\t\t\t\t\t\t\tif (ApptBoxPanel.isStrike(entity, gc.getTime())) {\n\t\t\t\t\t\t\t\t\tif (Prefs\n\t\t\t\t\t\t\t\t\t\t\t.getBoolPref(PrefName.HIDESTRIKETHROUGH))\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\t\t\t// draw strike-through text\n\t\t\t\t\t\t\t\t\t// need to use AttributedString to work\n\t\t\t\t\t\t\t\t\t// around a bug\n\t\t\t\t\t\t\t\t\tAttributedString as = new AttributedString(\n\t\t\t\t\t\t\t\t\t\t\ttext, stmap);\n\t\t\t\t\t\t\t\t\tg2.drawString(as.getIterator(), apptx,\n\t\t\t\t\t\t\t\t\t\t\tappty);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// draw the entity text\n\t\t\t\t\t\t\t\t\tg2.drawString(text, apptx, appty);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// increment the Y coord\n\t\t\t\t\t\t\t\tappty += smfontHeight;\n\n\t\t\t\t\t\t\t\t// reset to black\n\t\t\t\t\t\t\t\tg2.setColor(Color.black);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tg2.setClip(s);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\n\t\t\t\t// draw date\n\t\t\t\tg2.setFont(def_font);\n\t\t\t\tg2.drawString(Integer.toString(date), colleft + fontDesent,\n\t\t\t\t\t\trowtop + fontHeight);\n\n\t\t\t}\n\n\t\t}\n\n\t\t// draw the lines last\n\n\t\t// top border\n\t\tg2.drawLine(0, caltop, calright, caltop);\n\t\tfor (int row = 0; row < 7; row++) {\n\t\t\tint rowtop = (row * rowheight) + daytop;\n\t\t\t// horizontal lines from below day names to bottom\n\t\t\tg2.drawLine(0, rowtop, calright, rowtop);\n\t\t}\n\n\t\tfor (int col = 0; col < 8; col++) {\n\t\t\tint colleft = (col * colwidth);\n\t\t\t// vertical lines\n\t\t\tg2.drawLine(colleft, caltop, colleft, calbot);\n\n\t\t}\n\n\t\treturn Printable.PAGE_EXISTS;\n\t}\n\n\t@Override\n\tprotected void paintComponent(Graphics g) {\n\t\tsuper.paintComponent(g);\n\t\ttry {\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.scale(prev_scale, prev_scale);\n\t\t\tdrawIt(g, getWidth() / prev_scale, getHeight() / prev_scale,\n\t\t\t\t\tgetWidth() / prev_scale - 90,\n\t\t\t\t\tgetHeight() / prev_scale - 126, 45, 63, 0);\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * print is called by the print service to print pages\n\t */\n\t@Override\n\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\tthrows PrinterException {\n\n\t\t// if we have exceeded the requested number of pages - tell the print\n\t\t// service that we are done\n\t\tif (pageIndex > pages_ - 1)\n\t\t\treturn Printable.NO_SUCH_PAGE;\n\n\t\treturn (drawIt(g, pageFormat.getWidth(), pageFormat.getHeight(),\n\t\t\t\tpageFormat.getImageableWidth(),\n\t\t\t\tpageFormat.getImageableHeight(), pageFormat.getImageableX(),\n\t\t\t\tpageFormat.getImageableY(), pageIndex));\n\t}\n\n\t/**\n\t * set the number of months (pages) to print\n\t * @param p number of pages\n\t */\n\tpublic void setPages(int p) {\n\t\tpages_ = p;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/NoteBox.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.Color;\nimport java.awt.AlphaComposite;\nimport java.awt.Component;\nimport java.awt.Font;\nimport java.awt.Graphics2D;\nimport java.awt.Rectangle;\nimport java.awt.Shape;\nimport java.awt.event.ActionListener;\nimport java.awt.font.TextAttribute;\nimport java.awt.geom.AffineTransform;\nimport java.awt.image.AffineTransformOp;\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\nimport java.text.AttributedString;\nimport java.text.DateFormat;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.Icon;\nimport javax.swing.ImageIcon;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPopupMenu;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.LinkModel;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.entity.KeyedEntity;\nimport net.sf.borg.model.entity.Link;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.ClipBoard;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.task.ProjectView;\nimport net.sf.borg.ui.task.TaskView;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * \n * A Note Box is used to draw an untimed event on the day/week/month panels\n * \n */\npublic class NoteBox extends Box implements Box.Draggable {\n\n\tprivate CalendarEntity bean = null; // entity associated with this box\n\n\tprivate final Date date; // date being displayed - not necessarily date of the\n\t// entity in the case of repeats\n\n\tprivate boolean hasLink = false; // does entity have a link\n\n\tprivate int oldFontHeight = -1; // involved with reszie of todo icon - see\n\t// draw()\n\n\tprivate JPopupMenu popmenu = null; // popup menu\n\n\tprivate Icon todoIcon = null; // icon to mark todos\n\tprivate Icon lockIcon = null; // icon to mark private items\n\n\tprivate String todoMarker = null; // textual todo marker\n\n\tprivate String noteText = null; // the text of this note box\n\tprivate String tooltipText = null; \n\n\t/**\n\t * constructor\n\t * \n\t * @param d\n\t *            date that the box is on\n\t * @param ap\n\t *            the calendar entity\n\t * @param bounds\n\t *            bounds\n\t * @param clip\n\t *            clip\n\t */\n\tpublic NoteBox(Date d, CalendarEntity ap, Rectangle bounds, Rectangle clip) {\n\t\tsuper(bounds, clip);\n\t\tbean = ap;\n\t\tdate = d;\n\n\t\t// set the todo marker from the prefs - whether it is an icon or text\n\t\tString iconname = Prefs.getPref(PrefName.UCS_MARKER);\n\t\tString use_marker = Prefs.getPref(PrefName.UCS_MARKTODO);\n\t\tif (use_marker.equals(\"true\")) {\n\t\t\tif (iconname.endsWith(\".gif\") || iconname.endsWith(\".jpg\")) {\n\t\t\t\ttodoIcon = IconHelper.getIcon(\n\t\t\t\t\t\t\"/resource/\" + iconname);\n\t\t\t} else {\n\t\t\t\ttodoMarker = iconname;\n\t\t\t}\n\t\t}\n\t\t\n\t\tlockIcon = IconHelper.getIcon(\n\t\t\t\t\"/resource/\" + \"lock16.png\");\n\n\t\t// set the link flag\n\t\tCollection<Link> atts;\n\t\ttry {\n\t\t\tatts = LinkModel.getReference().getLinks((KeyedEntity<?>) bean);\n\t\t\tif (atts != null && atts.size() > 0)\n\t\t\t\thasLink = true;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\t// appointments need a special format\n\t\tif (ap instanceof Appointment)\n\t\t\tnoteText = AppointmentTextFormat.format((Appointment) ap, d);\n\t\telse\n\t\t\tnoteText = ap.getText();\n\t\t\n\t\tif (ap instanceof Appointment)\n\t\t\ttooltipText = AppointmentTextFormat.format((Appointment) ap, d, true);\n\t\telse\n\t\t\ttooltipText = ap.getText();\n\n\t}\n\n\t@Override\n\tpublic void delete() {\n\t\tif (bean instanceof Appointment)\n\t\t\tAppointmentModel.getReference().delAppt(\n\t\t\t\t\t((Appointment) bean).getKey());\n\t}\n\n\t/**\n\t * draw the box\n\t */\n\t@Override\n\tpublic void draw(Graphics2D g2, Component comp) {\n\n\t\tShape s = g2.getClip();\n\t\tif (clip != null)\n\t\t\tg2.setClip(clip);\n\n\t\tFont sm_font = g2.getFont();\n\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\n\t\t// resize todoIcon if needed to match the text size\n\t\tif (oldFontHeight != smfontHeight) {\n\t\t\tif (todoIcon != null) {\n\t\t\t\ttry {\n\t\t\t\t\t// get image\n\t\t\t\t\tBufferedImage image1 = ImageIO.read(getClass().getResource(\n\t\t\t\t\t\t\t\"/resource/\" + Prefs.getPref(PrefName.UCS_MARKER)));\n\t\t\t\t\tdouble size = image1.getHeight();\n\n\t\t\t\t\t// scale to 1/2 font height\n\t\t\t\t\tdouble scale = smfontHeight / (2 * size);\n\t\t\t\t\tAffineTransform tx = AffineTransform.getScaleInstance(\n\t\t\t\t\t\t\tscale, scale);\n\t\t\t\t\tAffineTransformOp op = new AffineTransformOp(tx,\n\t\t\t\t\t\t\tAffineTransformOp.TYPE_BICUBIC);\n\t\t\t\t\tBufferedImage rImage = op.filter(image1, null);\n\t\t\t\t\ttodoIcon = new ImageIcon(rImage);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\t// get image\n\t\t\t\t\tBufferedImage image1 = ImageIO.read(getClass().getResource(\n\t\t\t\t\t\t\t\"/resource/\" + \"lock16.png\"));\n\t\t\t\t\tdouble size = image1.getHeight();\n\n\t\t\t\t\t// scale to 1/2 font height\n\t\t\t\t\tdouble scale = smfontHeight / (2 * size);\n\t\t\t\t\tAffineTransform tx = AffineTransform.getScaleInstance(\n\t\t\t\t\t\t\tscale, scale);\n\t\t\t\t\tAffineTransformOp op = new AffineTransformOp(tx,\n\t\t\t\t\t\t\tAffineTransformOp.TYPE_BICUBIC);\n\t\t\t\t\tBufferedImage rImage = op.filter(image1, null);\n\t\t\t\t\tlockIcon = new ImageIcon(rImage);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\toldFontHeight = smfontHeight;\n\t\t}\n\n\t\t// create the strike-through text map\n\t\tMap<TextAttribute, Serializable> stmap = new HashMap<TextAttribute, Serializable>();\n\t\tstmap.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);\n\t\tstmap.put(TextAttribute.FONT, sm_font);\n\n\t\tTheme t = Theme.getCurrentTheme();\n\t\t\n\t\t// Draw oval pill background\n\t\tint cornerRadius = bounds.height / 2;  // Creates pill-shaped ends\n\n\t\tg2.setColor(new Color(t.colorFromString(getTextColor())));\n\n\t\t// Only draw background if not strike-through\n\t\tif (!getTextColor().equals(\"strike\") && t.isPills()) {\n\t\t\t// Apply slight transparency for better visual appeal\n\t\t\tg2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));\n\t\t\tg2.fillRoundRect(bounds.x + 1, bounds.y + 4, bounds.width - 2, bounds.height, \n\t\t\t\t\t\t\tcornerRadius, cornerRadius);\n\t\t\tg2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));\n\t\t}\n\n\t\t// use white background to highlight selected box\n\t\tif (isSelected == true) {\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\t\t\tg2.fillRoundRect(bounds.x + 1, bounds.y + 4, bounds.width - 2, bounds.height, \n\t\t\t\t\t\t\tcornerRadius, cornerRadius);\n\t\t}\n\n\t\t\n\t\t// default\n\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t// set alternate text color if needed\n\t\tif (getTextColor().equals(\"strike\")) {\n\n\t\t\tif( isSelected == true)\n\t\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tAttributedString as = new AttributedString(getText(), stmap);\n\t\t\tg2.drawString(as.getIterator(), bounds.x + 2, bounds.y\n\t\t\t\t\t+ smfontHeight);\n\t\t} else {\n\t\t\t// change color for a single appointment based on\n\t\t\t// its color - only if color print option set\n\t\t\tif (isSelected == true)\n\t\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\telse\n\t\t\t\tg2.setColor(new Color(t.colorFromString(getTextColor())));\n\t\t\t\n\n\t\t\t// preprend link indicator if needed\n\t\t\tint offset = 2;\n\t\t\tString text = getText();\n\t\t\tif (hasLink) {\n\t\t\t\ttext = \"@ \" + text;\n\t\t\t}\n\n\t\t\t// preprend todo marker if needed and draw the box text\n\t\t\tif (isTodo() && todoIcon != null) {\n\t\t\t\ttodoIcon.paintIcon(comp, g2, bounds.x + offset, bounds.y\n\t\t\t\t\t\t+ bounds.height / 2);\n\t\t\t\toffset = todoIcon.getIconWidth();\n\t\t\t} else if (isTodo() && todoMarker != null) {\n\t\t\t\ttext = todoMarker + \" \" + text;\n\t\t\t}\n\t\t\tif( bean.isPrivate() )\n\t\t\t{\n\t\t\t\tlockIcon.paintIcon(comp, g2, bounds.x + offset, bounds.y\n\t\t\t\t\t\t+ bounds.height / 2);\n\t\t\t\toffset += lockIcon.getIconWidth();\n\t\t\t}\n\t\t\tg2.drawString(text, bounds.x + offset, bounds.y + smfontHeight);\n\t\t\tg2.setColor(Color.black);\n\t\t}\n\n\t\tg2.setClip(s);\n\t\tg2.setColor(Color.black);\n\t}\n\n\t/**\n\t * get the popup menu\n\t */\n\t@Override\n\tpublic JPopupMenu getMenu() {\n\n\t\t// don't show a popup for non-appointments (i.e. tasks)\n\t\tif (!(bean instanceof Appointment))\n\t\t\treturn null;\n\n\t\tJMenuItem mnuitm;\n\t\tif (popmenu == null) {\n\t\t\tpopmenu = new JPopupMenu();\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Edit\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\tonClick();\n\t\t\t\t}\n\t\t\t});\n\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t.getResourceString(\"Delete\")));\n\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\n\t\t\t\t\tint ret = -1;\n\t\t\t\t\tif( bean instanceof Appointment && AppointmentModel.getReference().next_todo((Appointment) bean, date) != null )\n\t\t\t\t\t{\n\t\t\t\t\t\tret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Future_Todo_Warn\"), Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Really_Delete_\") + \"?\",\n\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.WARNING_MESSAGE);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Really_Delete_\")\n\t\t\t\t\t\t\t\t+ \"?\", Resource\n\t\t\t\t\t\t\t\t.getResourceString(\"Confirm_Delete\"),\n\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\tdelete();\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (bean instanceof Appointment) {\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Copy\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tAppointment appt = (Appointment) bean;\n\t\t\t\t\t\tClipBoard.getReference().put(appt.copy());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (isTodo()) {\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Done_(No_Delete)\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().do_todo(\n\t\t\t\t\t\t\t\t\t((Appointment) bean).getKey(), false, date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Done_(Delete)\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().do_todo(\n\t\t\t\t\t\t\t\t\t((Appointment) bean).getKey(), true, date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (bean instanceof Appointment\n\t\t\t\t\t&& Repeat.isRepeating((Appointment) bean)) {\n\t\t\t\tpopmenu.add(mnuitm = new JMenuItem(Resource\n\t\t\t\t\t\t.getResourceString(\"Delete_One_Only\")));\n\t\t\t\tmnuitm.addActionListener(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tAppointmentModel.getReference().delOneOnly(\n\t\t\t\t\t\t\t\t\t((Appointment) bean).getKey(), date);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn popmenu;\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn noteText;\n\t}\n\n\t/**\n\t * get the text color\n\t * \n\t * @return the text color\n\t */\n\tprivate String getTextColor() {\n\t\tif (bean == null)\n\t\t\treturn null;\n\n\t\tif (ApptBoxPanel.isStrike(bean, date)) {\n\t\t\treturn (\"strike\");\n\t\t}\n\n\t\treturn bean.getColor();\n\t}\n\n\t/**\n\t * check if is todo\n\t * \n\t * @return true if this box is for a todo\n\t */\n\tprivate boolean isTodo() {\n\t\treturn bean.isTodo();\n\t}\n\n\t/**\n\t * react to a drag of this box on the UI\n\t */\n\t@Override\n\tpublic void move(int real_time, Date d) throws Exception {\n\n\t\tint realtime = real_time;\n\t\tif (bean instanceof Appointment) {\n\t\t\t// change appointment date based on move\n\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\t((Appointment) bean).getKey());\n\n\t\t\t// if staying untimed then keep start at 12AM\n\t\t\tif (realtime == -1)\n\t\t\t\trealtime = 0;\n\n\t\t\tint hour = realtime / 60;\n\t\t\tint min = realtime % 60;\n\n\t\t\tint olddate = DateUtil.dayOfEpoch(ap.getDate());\n\n\t\t\tGregorianCalendar newCal = new GregorianCalendar();\n\t\t\tnewCal.setTime(d);\n\n\t\t\tif (hour != 0 || min != 0) {\n\n\t\t\t\t// we are moving to be timed - set duration\n\t\t\t\tap.setDuration(Integer.valueOf(15));\n\t\t\t\tap.setUntimed(\"N\");\n\t\t\t\tnewCal.set(Calendar.HOUR_OF_DAY, hour);\n\t\t\t\tint roundMin = (min / 5) * 5;\n\t\t\t\tnewCal.set(Calendar.MINUTE, roundMin);\n\n\t\t\t} else {\n\n\t\t\t\t// keep time and duration the same\n\t\t\t\tCalendar oldCal = new GregorianCalendar();\n\t\t\t\toldCal.setTime(ap.getDate());\n\t\t\t\tnewCal.set(Calendar.HOUR_OF_DAY,\n\t\t\t\t\t\toldCal.get(Calendar.HOUR_OF_DAY));\n\t\t\t\tnewCal.set(Calendar.MINUTE, oldCal.get(Calendar.MINUTE));\n\t\t\t\tnewCal.set(Calendar.SECOND, 0);\n\t\t\t}\n\n\t\t\tDate newTime = newCal.getTime();\n\t\t\tint newdate = DateUtil.dayOfEpoch(newTime);\n\t\t\t\n\n\t\t\t// check if user is moving a repeating appointment\n\t\t\tif (olddate != newdate && Repeat.isRepeating(ap)) {\n\t\t\t\t// if date change is not the first in the sequence, move appointments to that day of the week \n\t\t\t\tint k2 = DateUtil.dayOfEpoch(date);\n\t\t\t\tif (olddate != k2) {\n\t\t\t\t\tint incdate = newdate - k2;\n\t                newCal.setTime(ap.getDate());\n\t\t\t\t\tnewCal.add(Calendar.DATE, incdate);\n\t\t\t\t\tnewTime = newCal.getTime();\n\t\t\t\t\t\n\t\t\t\t\t // move next todo by the same amount\n\t                Date nt = ap.getNextTodo();\n\t                if( nt != null )\n\t                {\n\t            \t\tGregorianCalendar newTodo = new GregorianCalendar();\n\t            \t\tnewTodo.setTime(nt);\n\t            \t\tnewTodo.add(Calendar.DATE, incdate);\n\t            \t\tap.setNextTodo(newTodo.getTime());\n\t                }\n\t\t\t\t}\n\t\t\t}\n\t\t\tap.setDate(newTime);\n\n\t\t\tAppointmentModel.getReference().saveAppt(ap);\n\n\t\t} else if (bean instanceof Task) {\n\n\t\t\t// when a task is dragged, change its due date\n\n\t\t\tTask task = TaskModel.getReference()\n\t\t\t\t\t.getTask(((Task) bean).getKey());\n\t\t\tDate origd = task.getDueDate();\n\t\t\ttask.setDueDate(d);\n\n\t\t\t// reject change if it was dragged before its start date\n\t\t\tif (task.getDueDate() != null\n\t\t\t\t\t&& DateUtil.isAfter(task.getStartDate(), task.getDueDate())) {\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"sd_dd_warn\"));\n\t\t\t}\n\n\t\t\tTaskModel.getReference().savetask(task);\n\t\t\tTaskModel.getReference().addLog(\n\t\t\t\t\ttask.getKey(),\n\t\t\t\t\tResource.getResourceString(\"DueDate\") + \" \"\n\t\t\t\t\t\t\t+ Resource.getResourceString(\"Change\")\n\t\t\t\t\t\t\t+ \": \" + DateFormat.getDateInstance().format(origd) + \" --> \" + DateFormat.getDateInstance().format(d));\n\n\t\t} else if (bean instanceof Subtask) {\n\n\t\t\t// when a subtask is dragged, change its due date\n\n\t\t\tSubtask subtask = TaskModel.getReference().getSubTask(\n\t\t\t\t\t((Subtask) bean).getKey());\n\t\t\tsubtask.setDueDate(d);\n\n\t\t\t// reject change if it was dragged before its start date\n\t\t\tif (subtask.getDueDate() != null\n\t\t\t\t\t&& DateUtil.isAfter(subtask.getStartDate(),\n\t\t\t\t\t\t\tsubtask.getDueDate())) {\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"sd_dd_warn\"));\n\t\t\t}\n\n\t\t\tTaskModel.getReference().saveSubTask(subtask);\n\n\t\t} else if (bean instanceof Project) {\n\n\t\t\t// when a project is dragged, change its due date\n\n\t\t\tProject project = TaskModel.getReference().getProject(\n\t\t\t\t\t((Project) bean).getKey());\n\t\t\tproject.setDueDate(d);\n\n\t\t\t// reject change if it was dragged before its start date\n\t\t\tif (project.getDueDate() != null\n\t\t\t\t\t&& DateUtil.isAfter(project.getStartDate(),\n\t\t\t\t\t\t\tproject.getDueDate())) {\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"sd_dd_warn\"));\n\t\t\t}\n\n\t\t\tTaskModel.getReference().saveProject(project);\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.calendar.Box#edit()\n\t */\n\t@Override\n\tpublic void onClick() {\n\t\tif (bean instanceof Appointment) {\n\t\t\t// appointment clicked - bring up the appt editor\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tcal.setTime(date);\n\t\t\t// create new appt list view for the day of the appt\n\t\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n\t\t\t\t\tcal.get(Calendar.DATE));\n\t\t\t// add appt list tab to main view\n\t\t\tag.showView();\n\t\t\t// set appt list to be editing the clicked appt\n\t\t\tag.showApp(((Appointment) bean).getKey());\n\n\t\t} else if (bean instanceof Project) {\n\t\t\tMultiView.getMainView().setView(ViewType.TASK);\n\t\t\tnew ProjectView((Project) bean, ProjectView.Action.CHANGE, null)\n\t\t\t\t\t.showView();\n\t\t} else if (bean instanceof Task) {\n\t\t\t// task clicked - show it\n\t\t\ttry {\n\t\t\t\tMultiView.getMainView().setView(ViewType.TASK);\n\t\t\t\tnew TaskView((Task) bean, TaskView.Action.CHANGE, null)\n\t\t\t\t\t\t.showView();\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (bean instanceof Subtask) {\n\t\t\t// subtask clicked - show its task\n\t\t\tMultiView.getMainView().setView(ViewType.TASK);\n\t\t\tint taskid = ((Subtask) bean).getTask().intValue();\n\t\t\tTask t;\n\t\t\ttry {\n\t\t\t\tt = TaskModel.getReference().getTask(taskid);\n\t\t\t\tnew TaskView(t, TaskView.Action.CHANGE, null).showView();\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic String getToolTipText() {\n\t\treturn tooltipText;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/TodoView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Rectangle;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Vector;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.ButtonGroup;\nimport javax.swing.DefaultListSelectionModel;\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSpinner;\nimport javax.swing.JTable;\nimport javax.swing.JTextField;\nimport javax.swing.JToggleButton;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.border.LineBorder;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.DefaultTableCellRenderer;\nimport javax.swing.table.TableCellRenderer;\n\nimport com.toedter.calendar.JDateChooser;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.Repeat;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.KeyedEntity;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.task.ProjectView;\nimport net.sf.borg.ui.task.TaskView;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PlainDateEditor;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * Dockable window that shows a sorted list of all todos, allows the user to\n * take action on the todos, and lets the user quickly enter a new todo\n */\npublic class TodoView extends DockableView implements Prefs.Listener, Module {\n\n\t/**\n\t * Adds user colors to the todo table\n\t */\n\tclass TodoTableCellRenderer extends DefaultTableCellRenderer {\n\n\t\tprivate TableCellRenderer originalRenderer = null;\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tTodoTableCellRenderer(TableCellRenderer orig) {\n\t\t\toriginalRenderer = orig;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see\n\t\t * javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent\n\t\t * (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)\n\t\t */\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table,\n\t\t\t\tObject value, boolean isSelected, boolean hasFocus, int row,\n\t\t\t\tint column) {\n\n\t\t\tComponent c = originalRenderer.getTableCellRendererComponent(table,\n\t\t\t\t\tvalue, isSelected, hasFocus, row, column);\n\n\t\t\tDateFormat sdf = DateFormat.getDateInstance();\n\n\t\t\tJLabel theTableCellComponent = (JLabel) c;\n\n\t\t\t// user colors\n\t\t\tif (user_colors) {\n\t\t\t\tif (isSelected) {\n\t\t\t\t\ttheTableCellComponent.setForeground(Color.BLACK);\n\t\t\t\t\ttheTableCellComponent.setBackground(Color.ORANGE);\n\t\t\t\t} else {\n\n\t\t\t\t\tTheme t = Theme.getCurrentTheme();\n\t\t\t\t\tString color = table.getModel().getValueAt(row, 3)\n\t\t\t\t\t\t\t.toString();\n\t\t\t\t\ttheTableCellComponent.setForeground(new Color(t.colorFromString(color)));\n\t\t\t\t\ttheTableCellComponent.setBackground(new Color(t\n\t\t\t\t\t\t\t.getWeekdayBg()));\n\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (column == 0) {\n\t\t\t\t// format dates\n\t\t\t\tString dateText = sdf.format(value);\n\t\t\t\tif (!DateUtil.isAfter((Date) value, new Date()))\n\t\t\t\t\tdateText = \"* \" + dateText;\n\t\t\t\ttheTableCellComponent.setText(dateText);\n\t\t\t} else if (column == 1) {\n\t\t\t\t// set tooltip to todo text\n\t\t\t\ttheTableCellComponent.setToolTipText((String)table.getModel().getValueAt(row, 6));\n\t\t\t}\n\t\t\t\n\t\t\tif( column == 2 || column == 3)\n\t\t\t\ttheTableCellComponent.setHorizontalAlignment(CENTER);\n\t\t\telse\n\t\t\t\ttheTableCellComponent.setHorizontalAlignment(LEFT);\n\n\t\t\treturn theTableCellComponent;\n\t\t}\n\t}\n\n\t/**\n\t * Icon to show on toggle buttons - no text is shown, only a color\n\t */\n\tstatic private class ToggleButtonIcon implements Icon {\n\n\t\tprivate Color color = Color.BLACK;\n\t\tstatic private final int height = 10;\n\t\tstatic private final int width = 30;\n\n\t\t/**\n\t\t * Instantiates a new toggle button icon.\n\t\t * \n\t\t * @param col\n\t\t *            the color\n\t\t */\n\t\tpublic ToggleButtonIcon(Color col) {\n\t\t\tcolor = col;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconHeight()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconHeight() {\n\t\t\treturn height;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconWidth()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconWidth() {\n\t\t\treturn width;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#paintIcon(java.awt.Component,\n\t\t * java.awt.Graphics, int, int)\n\t\t */\n\t\t@Override\n\t\tpublic void paintIcon(Component c, Graphics g, int x, int y) {\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setColor(Color.BLACK);\n\t\t\tg2.drawRect(x, y, width, height);\n\t\t\tg2.setColor(color);\n\t\t\tg2.fillRect(x, y, width, height);\n\t\t}\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The change date action. */\n\tprivate final ActionListener changeDateAction = new ActionListener() {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\tList<Appointment> aplist = new ArrayList<Appointment>();\n\t\t\tfor (KeyedEntity<?> ke : getSelectedItems(true)) {\n\t\t\t\taplist.add((Appointment) ke);\n\t\t\t}\n\t\t\tAppointmentListView.onChangeDate(aplist);\n\t\t}\n\t};\n\n\t/** The done delete action. */\n\tprivate final ActionListener doneDeleteAction = new ActionListener() {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\tdoTodoAction(true);\n\t\t}\n\t};\n\n\t/** The done no delete action. */\n\tprivate final ActionListener doneNoDeleteAction = new ActionListener() {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\tdoTodoAction(false);\n\t\t}\n\t};\n\n\t/** The move to following day action. */\n\tprivate final ActionListener moveToFollowingDayAction = new ActionListener() {\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\tList<Appointment> aplist = new ArrayList<Appointment>();\n\t\t\tfor (KeyedEntity<?> ke : getSelectedItems(true)) {\n\t\t\t\taplist.add((Appointment) ke);\n\t\t\t}\n\t\t\tAppointmentListView.onMoveToFollowingDay(aplist);\n\t\t}\n\t};\n\n\t/** The category combo box. */\n\tprivate JComboBox<String> categoryComboBox;\n\n\t/** The black toggle button. */\n\tprivate JToggleButton blackToggleButton;\n\n\t/** The blue toggle button. */\n\tprivate JToggleButton blueToggleButton;\n\n\t/** The green toggle button. */\n\tprivate JToggleButton greenToggleButton;\n\n\t/** The red toggle button. */\n\tprivate JToggleButton redToggleButton;\n\n\t/** The white toggle button. */\n\tprivate JToggleButton whiteToggleButton;\n\n\t/** The todo list. */\n\tprivate Collection<KeyedEntity<?>> theTodoList;\n\n\t/** The todo date. */\n\tprivate JDateChooser todoDate;\n\n\t/** The todo table. */\n\tprivate JTable todoTable;\n\n\t/** The todo text. */\n\tprivate JTextField todoText;\n\n\t/** Spinner for todo priority. */\n\tprivate JSpinner todoPrioritySpinner;\n\n\t/** cached copy of user-colors preference */\n\tprivate boolean user_colors = false;\n\n\tprivate JLabel totalLabel;\n\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * Instantiates a new todo view.\n\t */\n\tpublic TodoView() {\n\n\t\tsuper();\n\n\t}\n\n\t/**\n\t * add a todo\n\t * \n\t */\n\tprivate void addTodoActionPerformed() {\n\n\t\t// get text and date\n\t\tString tdtext = todoText.getText();\n\t\tCalendar c = todoDate.getCalendar();\n\n\t\t// auto default blank date to today's date.\n\t\tif (c == null) {\n\t\t\tif (Prefs\n\t\t\t\t\t.getBoolPref(PrefName.TODO_QUICK_ENTRY_AUTO_SET_DATE_FIELD)) {\n\t\t\t\tc = new GregorianCalendar();\n\t\t\t\tc.setTime(new Date());\n\t\t\t}\n\t\t}\n\n\t\t// warn the user if text or date is missing\n\t\tif (tdtext.trim().isEmpty() || c == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"todomissingdata\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// load up a default appt from any saved prefs\n\t\tAppointment appt = AppointmentModel\n\t\t\t\t.getDefaultAppointment();\n\t\tif (appt == null)\n\t\t\tappt = AppointmentModel.getReference().newAppt();\n\n\t\t// set the date\n\t\tc.set(Calendar.HOUR, 0);\n\t\tc.set(Calendar.MINUTE, 0);\n\t\tc.set(Calendar.SECOND, 0);\n\t\tc.set(Calendar.AM_PM, Calendar.AM);\n\t\tappt.setDate(c.getTime());\n\n\t\t// set text\n\t\tappt.setText(tdtext);\n\n\t\t// todo flag\n\t\tappt.setTodo(true);\n\n\t\t// not private\n\t\tappt.setPrivate(false);\n\n\t\t// set color\n\t\tif (redToggleButton.isSelected())\n\t\t\tappt.setColor(\"red\");\n\t\telse if (blueToggleButton.isSelected())\n\t\t\tappt.setColor(\"blue\");\n\t\telse if (greenToggleButton.isSelected())\n\t\t\tappt.setColor(\"green\");\n\t\telse if (whiteToggleButton.isSelected())\n\t\t\tappt.setColor(\"white\");\n\t\telse\n\t\t\tappt.setColor(\"black\");\n\n\t\t// no repeating\n\t\tappt.setFrequency(Repeat.ONCE);\n\t\tappt.setTimes(Integer.valueOf(1));\n\t\tappt.setRepeatFlag(false);\n\n\t\t// set category\n\t\tString cat = (String) categoryComboBox.getSelectedItem();\n\t\tif (cat.equals(\"\") || cat.equals(CategoryModel.UNCATEGORIZED)) {\n\t\t\tappt.setCategory(null);\n\t\t} else {\n\t\t\tappt.setCategory(cat);\n\t\t}\n\n\t\tappt.setPriority((Integer) todoPrioritySpinner.getValue());\n\t\tAppointmentModel.getReference().saveAppt(appt);\n\n\t\t// clear the contents of the todo text field\n\t\t// if the preference is set.\n\t\tif (Prefs.getBoolPref(PrefName.TODO_QUICK_ENTRY_AUTO_CLEAR_TEXT_FIELD)) {\n\t\t\ttodoText.setText(\"\");\n\t\t}\n\n\t\trequestFocus();\n\n\t}\n\n\tprivate boolean allAppointmentsSelected() {\n\t\tList<KeyedEntity<?>> items = getSelectedItems(false);\n\t\tfor (int i = 0; i < items.size(); ++i) {\n\t\t\tObject o = items.get(i);\n\t\t\tif (!(o instanceof Appointment)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * mark selected items as done. For appointments, mark the todo as done. For\n\t * task, projects, subtasks, mark the item as closed\n\t * \n\t * @param del\n\t *            if true and it is an appointment, delete the appointment when\n\t *            all occurrences are done\n\t */\n\tprivate void doTodoAction(boolean del) {\n\n\t\t// get the selected items\n\t\tList<KeyedEntity<?>> items = getSelectedItems(false);\n\t\tfor (int i = 0; i < items.size(); ++i) {\n\t\t\ttry {\n\t\t\t\t// take different action depending on the entity type\n\t\t\t\tObject o = items.get(i);\n\t\t\t\tif (o instanceof Appointment) {\n\t\t\t\t\tint key = ((Appointment) o).getKey();\n\t\t\t\t\tAppointmentModel.getReference().do_todo(key, del);\n\t\t\t\t} else if (o instanceof Project) {\n\t\t\t\t\tProject p = (Project) o;\n\t\t\t\t\tTaskModel.getReference().closeProject(p.getKey());\n\t\t\t\t} else if (o instanceof Task) {\n\t\t\t\t\tTask t = (Task) o;\n\t\t\t\t\tTaskModel.getReference().close(t.getKey());\n\t\t\t\t} else if (o instanceof Subtask) {\n\t\t\t\t\tSubtask s = (Subtask) o;\n\t\t\t\t\ts.setCloseDate(new Date());\n\t\t\t\t\tTaskModel.getReference().saveSubTask(s);\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\n\t\tif (!isInitialized) {\n\t\t\t// listen for pref changes\n\t\t\tPrefs.addListener(this);\n\n\t\t\t// listen for appt and task model changes\n\t\t\taddModel(AppointmentModel.getReference());\n\t\t\taddModel(TaskModel.getReference());\n\t\t\taddModel(CategoryModel.getReference());\n\n\t\t\t// init the gui components\n\t\t\tinitComponents();\n\n\t\t\t// the todos will be displayed in a sorted table with 2 columns -\n\t\t\t// data and todo text\n\t\t\ttodoTable.setModel(new TableSorter(new String[] {\n\t\t\t\t\tResource.getResourceString(\"Date\"),\n\t\t\t\t\tResource.getResourceString(\"To_Do\"),\n\t\t\t\t\tResource.getResourceString(\"Category\"),\n\t\t\t\t\tResource.getResourceString(\"Color\"), \"key\",\n\t\t\t\t\tResource.getResourceString(\"Priority\"), \"tooltip\" }, new Class[] {\n\t\t\t\t\tDate.class, java.lang.String.class, java.lang.String.class,\n\t\t\t\t\tjava.lang.String.class, java.lang.Integer.class,\n\t\t\t\t\tjava.lang.Integer.class, java.lang.String.class }));\n\n\t\t\ttodoTable.getColumnModel().getColumn(0).setPreferredWidth(140);\n\t\t\ttodoTable.getColumnModel().getColumn(1).setPreferredWidth(400);\n\t\t\ttodoTable.getColumnModel().getColumn(2).setPreferredWidth(120);\n\t\t\ttodoTable.getColumnModel().getColumn(5).setPreferredWidth(50);\n\n\t\t\tuser_colors = Prefs.getBoolPref(PrefName.UCS_ONTODO);\n\n\t\t\t// set user color renderer\n\t\t\tTableCellRenderer defaultObjectRenderer = todoTable\n\t\t\t\t\t.getDefaultRenderer(Object.class);\n\t\t\ttodoTable.setDefaultRenderer(Object.class,\n\t\t\t\t\tnew TodoTableCellRenderer(defaultObjectRenderer));\n\t\t\tTableCellRenderer defaultDateRenderer = todoTable\n\t\t\t\t\t.getDefaultRenderer(Date.class);\n\t\t\ttodoTable.setDefaultRenderer(Date.class, new TodoTableCellRenderer(\n\t\t\t\t\tdefaultDateRenderer));\n\t\t\tTableCellRenderer defaultIntegerRenderer = todoTable\n\t\t\t\t\t.getDefaultRenderer(Integer.class);\n\t\t\ttodoTable.setDefaultRenderer(Integer.class,\n\t\t\t\t\tnew TodoTableCellRenderer(defaultIntegerRenderer));\n\n\t\t\t// remove the hidden columns - color and key and tooltip\n\t\t\ttodoTable.removeColumn(todoTable.getColumnModel().getColumn(3));\n\t\t\ttodoTable.removeColumn(todoTable.getColumnModel().getColumn(3));\n\t\t\ttodoTable.removeColumn(todoTable.getColumnModel().getColumn(4));\n\n\t\t\trefresh();\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.DockableView#getFrameTitle()\n\t */\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn Resource.getResourceString(\"To_Do_List\");\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"To_Do\");\n\t}\n\n\t/**\n\t * Gets the selected items.\n\t * \n\t * @param appts_only\n\t *            if true, return appointments only\n\t * \n\t * @return the selected items\n\t */\n\tprivate List<KeyedEntity<?>> getSelectedItems(boolean appts_only) {\n\t\tList<KeyedEntity<?>> lst = new ArrayList<KeyedEntity<?>>();\n\t\tint[] indices = todoTable.getSelectedRows();\n\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\tint index = indices[i];\n\t\t\ttry {\n\n\t\t\t\t// need to ask the table for the original (befor sorting) index\n\t\t\t\t// of the selected row\n\t\t\t\tTableSorter tm = (TableSorter) todoTable.getModel();\n\t\t\t\tint k = tm.getMappedIndex(index);\n\n\t\t\t\tObject o = theTodoList.toArray()[k];\n\t\t\t\tif (!appts_only || o instanceof Appointment) {\n\t\t\t\t\tlst.add((KeyedEntity<?>) o);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\n\t\treturn lst;\n\t}\n\n\tprivate JLabel getTotalLabel() {\n\t\tif (totalLabel == null) {\n\t\t\ttotalLabel = new JLabel();\n\t\t}\n\t\treturn totalLabel;\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.TODO;\n\t}\n\n\t/**\n\t * Inits the components.\n\t */\n\tprivate void initComponents() {\n\n\t\t// create widgets (all bunched up because of code generation - not worth\n\t\t// cleanup up\n\t\tJScrollPane tableScroll = new JScrollPane();\n\t\ttodoTable = new JTable();\n\t\ttodoText = new JTextField();\n\n\t\t// add keyboard shortcut to do add todo item when enter pressed on the\n\t\t// text field.\n\t\ttodoText.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent event) {\n\t\t\t\tif (event.getKeyCode() == KeyEvent.VK_ENTER) {\n\t\t\t\t\taddTodoActionPerformed();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\ttodoDate = new JDateChooser(new PlainDateEditor());\n\n\t\tJButton addTodoButton = new JButton();\n\t\tJLabel todoLabel = new JLabel();\n\t\tJLabel dateLabel = new JLabel();\n\t\tcategoryComboBox = new JComboBox<String>();\n\n\t\t// initial toggle button settings\n\t\tredToggleButton = new JToggleButton(\"\", false);\n\t\tblueToggleButton = new JToggleButton(\"\", false);\n\t\tgreenToggleButton = new JToggleButton(\"\", false);\n\t\tblackToggleButton = new JToggleButton(\"\", true);\n\t\twhiteToggleButton = new JToggleButton(\"\", false);\n\n\t\t// initial priority spinner settings\n\t\tint val = 5;\n\t\tint min = 1;\n\t\tint max = 10;\n\t\tint step = 1;\n\t\ttodoPrioritySpinner = new JSpinner(new SpinnerNumberModel(val, min,\n\t\t\t\tmax, step));\n\n\t\tloadCategories();\n\n\t\t// *******************************************************************\n\t\t// main table\n\t\t// *******************************************************************\n\t\ttodoTable.setBorder(new LineBorder(new Color(0, 0, 0)));\n\t\tDefaultListSelectionModel mylsmodel = new DefaultListSelectionModel();\n\t\tmylsmodel\n\t\t\t\t.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\t\ttodoTable.setSelectionModel(mylsmodel);\n\t\ttodoTable.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(MouseEvent evt) {\n\t\t\t\tif (evt.getClickCount() < 2)\n\t\t\t\t\treturn;\n\t\t\t\tonEditTodo();\n\t\t\t}\n\t\t});\n\n\t\ttableScroll.setViewportView(todoTable);\n\t\ttodoTable.setShowGrid(true);\n\t\ttodoTable.setIntercellSpacing(new Dimension(1, 1));\n\n\n\t\t// *******************************************************************\n\t\t// quick entry panel\n\t\t// *******************************************************************\n\t\tJPanel quickEntryPanel = new JPanel();\n\t\tquickEntryPanel.setLayout(new GridBagLayout());\n\t\tquickEntryPanel.setBorder(BorderFactory.createTitledBorder(null,\n\t\t\t\tResource.getResourceString(\"todoquickentry\"),\n\t\t\t\tTitledBorder.DEFAULT_JUSTIFICATION,\n\t\t\t\tTitledBorder.DEFAULT_POSITION, null, null));\n\t\taddTodoButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Save16.gif\"));\n\t\tResourceHelper.setText(addTodoButton, \"Add\");\n\t\taddTodoButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\taddTodoActionPerformed();\n\t\t\t}\n\t\t});\n\n\t\tJPanel toggleButtonPanel = new JPanel();\n\t\ttoggleButtonPanel.setLayout(new FlowLayout());\n\t\ttoggleButtonPanel.add(redToggleButton);\n\t\ttoggleButtonPanel.add(blueToggleButton);\n\t\ttoggleButtonPanel.add(greenToggleButton);\n\t\ttoggleButtonPanel.add(blackToggleButton);\n\t\ttoggleButtonPanel.add(whiteToggleButton);\n\n\t\tTheme t = Theme.getCurrentTheme();\n\t\tredToggleButton.setIcon(new ToggleButtonIcon(new Color(t\n\t\t\t\t.getTextColor1())));\n\n\t\tblueToggleButton.setIcon(new ToggleButtonIcon(new Color(t\n\t\t\t\t.getTextColor2())));\n\n\t\tgreenToggleButton.setIcon(new ToggleButtonIcon(new Color(t\n\t\t\t\t.getTextColor3())));\n\n\t\tblackToggleButton.setIcon(new ToggleButtonIcon(new Color(t\n\t\t\t\t.getTextColor4())));\n\n\t\twhiteToggleButton.setIcon(new ToggleButtonIcon(new Color(t\n\t\t\t\t.getTextColor5())));\n\n\t\tButtonGroup mutator = new ButtonGroup();\n\t\tmutator.add(redToggleButton);\n\t\tmutator.add(blueToggleButton);\n\t\tmutator.add(greenToggleButton);\n\t\tmutator.add(blackToggleButton);\n\t\tmutator.add(whiteToggleButton);\n\n\t\tJLabel categoryLabel = new JLabel();\n\t\tResourceHelper.setText(categoryLabel, \"Category\");\n\t\tcategoryLabel.setLabelFor(categoryComboBox);\n\n\t\ttoggleButtonPanel.add(categoryLabel);\n\t\ttoggleButtonPanel.add(categoryComboBox);\n\n\t\tJLabel priorityLabel = new JLabel();\n\t\tResourceHelper.setText(priorityLabel, \"Priority\");\n\t\tpriorityLabel.setLabelFor(todoPrioritySpinner);\n\n\t\ttoggleButtonPanel.add(priorityLabel);\n\t\ttoggleButtonPanel.add(todoPrioritySpinner);\n\n\t\tResourceHelper.setText(todoLabel, \"To_Do\");\n\t\ttodoLabel.setLabelFor(todoText);\n\t\tResourceHelper.setText(dateLabel, \"Date\");\n\t\tdateLabel.setLabelFor(todoDate);\n\n\t\tquickEntryPanel.add(todoLabel, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.HORIZONTAL));\n\t\tquickEntryPanel.add(todoText, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.HORIZONTAL, 10.0, 0.0));\n\t\tquickEntryPanel.add(toggleButtonPanel, GridBagConstraintsFactory\n\t\t\t\t.create(0, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tquickEntryPanel.add(dateLabel, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.HORIZONTAL, 1.0, 0.0));\n\t\tquickEntryPanel.add(todoDate, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.HORIZONTAL));\n\t\tquickEntryPanel.add(addTodoButton,\n\t\t\t\tGridBagConstraintsFactory.create(2, 1));\n\n\t\t// *******************************************************************\n\t\t// popup menu\n\t\t// *******************************************************************\n\t\tActionListener alEdit = new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tonEditTodo();\n\t\t\t}\n\t\t};\n\n\t\tfinal JPopupMenu apptPopupMenu = PopupMenuHelper\n\t\t\t\t.createPopupMenu(new PopupMenuHelper.Entry[] {\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(doneDeleteAction,\n\t\t\t\t\t\t\t\t\"Done_(Delete)\"),\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(doneNoDeleteAction,\n\t\t\t\t\t\t\t\t\"Done_(No_Delete)\"),\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(alEdit, \"Edit\"),\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(moveToFollowingDayAction,\n\t\t\t\t\t\t\t\t\"Move_To_Following_Day\"),\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(changeDateAction,\n\t\t\t\t\t\t\t\t\"changedate\") });\n\n\t\tfinal JPopupMenu todoPopupMenu = PopupMenuHelper\n\t\t\t\t.createPopupMenu(new PopupMenuHelper.Entry[] {\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(doneDeleteAction,\n\t\t\t\t\t\t\t\t\"Done_(No_Delete)\"),\n\t\t\t\t\t\tnew PopupMenuHelper.Entry(alEdit, \"Edit\") });\n\n\t\ttodoTable.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\tcase 0x020D:\n\t\t\t\t\tint[] selIndices = todoTable.getSelectedRows();\n\t\t\t\t\tif (selIndices.length == 0)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tint rowIndex = selIndices[0];\n\t\t\t\t\tRectangle rct = todoTable.getCellRect(rowIndex, 0, false);\n\t\t\t\t\tif (getSelectedItems(false).isEmpty())\n\t\t\t\t\t\treturn;\n\t\t\t\t\tif (allAppointmentsSelected())\n\t\t\t\t\t\tapptPopupMenu\n\t\t\t\t\t\t\t\t.show(todoTable, rct.x, rct.y + rct.height);\n\t\t\t\t\telse\n\t\t\t\t\t\ttodoPopupMenu\n\t\t\t\t\t\t\t\t.show(todoTable, rct.x, rct.y + rct.height);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\ttodoTable.addMouseListener(new MouseAdapter() {\n\t\t\tprivate void maybeShowPopup(MouseEvent e) {\n\t\t\t\tif (e.isPopupTrigger()) {\n\t\t\t\t\tint row = todoTable.rowAtPoint(e.getPoint());\n\t\t\t\t\tif (row != -1 && !todoTable.isRowSelected(row)) {\n\t\t\t\t\t\ttodoTable.getSelectionModel().setSelectionInterval(row,\n\t\t\t\t\t\t\t\trow);\n\t\t\t\t\t}\n\t\t\t\t\tif (getSelectedItems(false).isEmpty())\n\t\t\t\t\t\treturn;\n\t\t\t\t\tif (allAppointmentsSelected())\n\t\t\t\t\t\tapptPopupMenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t\telse\n\t\t\t\t\t\ttodoPopupMenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tmaybeShowPopup(e);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\t\tmaybeShowPopup(e);\n\t\t\t}\n\t\t});\n\n\t\t// *******************************************************************\n\t\t// button panel\n\t\t// *******************************************************************\n\t\tJPanel buttonPanel = new JPanel();\n\n\t\tJButton doneButton = new JButton();\n\t\tResourceHelper.setText(doneButton, \"Done_(No_Delete)\");\n\t\tdoneButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tdoTodoAction(false);\n\t\t\t}\n\t\t});\n\t\tdoneButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Properties16.gif\"));\n\t\tbuttonPanel.add(doneButton, null);\n\n\t\tJButton doneDeleteButton = new JButton();\n\t\tResourceHelper.setText(doneDeleteButton, \"Done_(Delete)\");\n\t\tdoneDeleteButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tdoTodoAction(true);\n\t\t\t}\n\t\t});\n\t\tdoneDeleteButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Delete16.gif\"));\n\t\tbuttonPanel.add(doneDeleteButton, null);\n\n\t\t// *******************************************************************\n\t\t// top level panel\n\t\t// *******************************************************************\n\t\tsetLayout(new GridBagLayout());\n\t\tadd(tableScroll, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\tJPanel medPanel = new JPanel(new BorderLayout());\n\n\t\tmedPanel.add(getTotalLabel(), BorderLayout.WEST);\n\t\tmedPanel.add(buttonPanel, BorderLayout.CENTER);\n\n\t\tadd(medPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\t\tadd(quickEntryPanel, GridBagConstraintsFactory.create(0, 2,\n\t\t\t\tGridBagConstraints.HORIZONTAL, 1.0, 0.0));\n\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\n\t\t\t\t\t\t\"/resource/Properties16.gif\"), getModuleName(),\n\t\t\t\tnew ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(getViewType());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(getViewType());\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void loadCategories() {\n\t\t// load categories\n\t\ttry {\n\t\t\tcategoryComboBox.removeAllItems();\n\t\t\tCollection<String> acats = CategoryModel.getReference()\n\t\t\t\t\t.getCategories();\n\t\t\tIterator<String> ait = acats.iterator();\n\t\t\twhile (ait.hasNext()) {\n\t\t\t\tcategoryComboBox.addItem(ait.next());\n\t\t\t}\n\t\t\tcategoryComboBox.setSelectedIndex(0);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * open a todo item for editing\n\t */\n\tprivate void onEditTodo() {\n\n\t\tint row = todoTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn;\n\n\t\t// Ensure only one row is selected.\n\t\ttodoTable.getSelectionModel().setSelectionInterval(row, row);\n\n\t\tTableSorter tm = (TableSorter) todoTable.getModel();\n\t\tint k = tm.getMappedIndex(row);\n\n\t\t// get the object and edit differently based on type\n\t\tObject o = theTodoList.toArray()[k];\n\t\tif (o instanceof Appointment) {\n\n\t\t\tDate d = (Date) tm.getValueAt(row, 0);\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tcal.setTime(d);\n\n\t\t\t// bring up an appt editor window\n\t\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n\t\t\t\t\tcal.get(Calendar.DATE));\n\t\t\tAppointment ap = (Appointment) o;\n\t\t\tag.showView();\n\n\t\t\tag.showApp(ap.getKey());\n\n\t\t} else if (o instanceof Project) {\n\t\t\ttry {\n\t\t\t\tnew ProjectView((Project) o, ProjectView.Action.CHANGE, null)\n\t\t\t\t\t\t.showView();\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (o instanceof Task) {\n\t\t\ttry {\n\t\t\t\tnew TaskView((Task) o, TaskView.Action.CHANGE, null).showView();\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (o instanceof Subtask) {\n\t\t\tint taskid = ((Subtask) o).getTask().intValue();\n\t\t\tTask t;\n\t\t\ttry {\n\t\t\t\tt = TaskModel.getReference().getTask(taskid);\n\t\t\t\tTaskView tskg = new TaskView(t, TaskView.Action.CHANGE, null);\n\t\t\t\ttskg.setVisible(true);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.common.Prefs.Listener#prefsChanged()\n\t */\n\t@Override\n\tpublic void prefsChanged() {\n\t\tuser_colors = Prefs.getBoolPref(PrefName.UCS_ONTODO);\n\t\trefresh();\n\t}\n\n\t/**\n\t * Prints the todo table\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tTablePrinter.printTable(todoTable);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * load todos from the model and show in the UI\n\t */\n\t@Override\n\tpublic void refresh() {\n\n\t\t// get the todos from the data model\n\t\ttheTodoList = new ArrayList<KeyedEntity<?>>();\n\t\ttheTodoList.addAll(AppointmentModel.getReference().get_todos());\n\n\t\t// init the table to empty\n\t\tTableSorter tm = (TableSorter) todoTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(todoTable);\n\t\ttm.setRowCount(0);\n\n\t\t// add the todo appointment rows to the table\n\t\tIterator<KeyedEntity<?>> tdit = theTodoList.iterator();\n\t\tint totalCount = 0;\n\t\twhile (tdit.hasNext()) {\n\t\t\tAppointment r = (Appointment) tdit.next();\n\n\t\t\ttry {\n\n\t\t\t\t// date is the next todo field if present, otherwise\n\t\t\t\t// the due date\n\t\t\t\tDate nt = r.getNextTodo();\n\t\t\t\tif (nt == null) {\n\t\t\t\t\tnt = r.getDate();\n\t\t\t\t}\n\n\t\t\t\t// get appt text\n\t\t\t\tString tx = AppointmentTextFormat.format(r, nt);\n\n\t\t\t\t// add the table row\n\t\t\t\tObject[] ro = new Object[7];\n\t\t\t\tro[0] = nt;\n\t\t\t\tro[1] = tx;\n\t\t\t\tro[2] = r.getCategory(); // category\n\n\t\t\t\t// color\n\t\t\t\tif (r.getColor() == null)\n\t\t\t\t\tro[3] = \"black\";\n\t\t\t\telse\n\t\t\t\t\tro[3] = r.getColor();\n\n\t\t\t\t// key\n\t\t\t\tro[4] = Integer.valueOf(r.getKey());\n\n\t\t\t\t// priority\n\t\t\t\tro[5] = r.getPriority();\n\t\t\t\t\n\t\t\t\t// tooltip\n\t\t\t\tro[6] = AppointmentTextFormat.format(r, nt, true);\n\n\t\t\t\ttm.addRow(ro);\n\t\t\t\ttotalCount++;\n\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tgetTotalLabel().setText(totalCount + \" items\");\n\t\t}\n\n\t\t// add open projects with a due date to the list\n\t\tString show_abb = Prefs.getPref(PrefName.TASK_SHOW_ABBREV);\n\t\tif (Prefs.getBoolPref(PrefName.CAL_SHOW_TASKS)) {\n\n\t\t\ttry {\n\t\t\t\tCollection<Project> pjs = TaskModel.getReference()\n\t\t\t\t\t\t.getProjects();\n\t\t\t\tIterator<Project> it = pjs.iterator();\n\t\t\t\twhile (it.hasNext()) {\n\n\t\t\t\t\tProject pj = it.next();\n\t\t\t\t\tif (pj.getDueDate() == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// skip closed projects\n\t\t\t\t\tif (pj.getStatus().equals(\n\t\t\t\t\t\t\tResource.getResourceString(\"CLOSED\")))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// filter by category\n\t\t\t\t\tif (!CategoryModel.getReference().isShown(pj.getCategory()))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// build a string for the table - with prefix if needed\n\t\t\t\t\tString abb = \"\";\n\t\t\t\t\tif (show_abb.equals(\"true\"))\n\t\t\t\t\t\tabb = \"PR\" + pj.getKey() + \" \";\n\t\t\t\t\tString todostring = abb + pj.getText();\n\n\t\t\t\t\tObject[] ro = new Object[7];\n\t\t\t\t\tro[0] = pj.getDueDate();\n\t\t\t\t\tro[1] = todostring;\n\t\t\t\t\tro[2] = pj.getCategory();\n\t\t\t\t\tif (pj.getColor() == null)\n\t\t\t\t\t\tro[3] = \"black\";\n\t\t\t\t\telse\n\t\t\t\t\t\tro[3] = pj.getColor();\n\t\t\t\t\tro[4] = null;\n\t\t\t\t\tro[5] = null;\n\t\t\t\t\tro[6] = todostring;\n\t\t\t\t\ttm.addRow(ro);\n\t\t\t\t\ttheTodoList.add(pj);\n\t\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// add open tasks with a due date to the list\n\t\t\tVector<Task> mrs = TaskModel.getReference().get_tasks();\n\t\t\tfor (int i = 0; i < mrs.size(); i++) {\n\n\t\t\t\tTask mr = mrs.elementAt(i);\n\t\t\t\tif (mr.getDueDate() == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// build a string for the table - with prefix if needed\n\t\t\t\t\tString abb = \"\";\n\t\t\t\t\tif (show_abb.equals(\"true\"))\n\t\t\t\t\t\tabb = \"BT\" + mr.getKey() + \" \";\n\t\t\t\t\tString btstring = abb + mr.getText();\n\n\t\t\t\t\tObject[] ro = new Object[7];\n\t\t\t\t\tro[0] = mr.getDueDate();\n\t\t\t\t\tro[1] = btstring;\n\t\t\t\t\tro[2] = mr.getCategory();\n\t\t\t\t\tif (mr.getColor() == null)\n\t\t\t\t\t\tro[3] = \"black\";\n\t\t\t\t\telse\n\t\t\t\t\t\tro[3] = mr.getColor();\n\t\t\t\t\tro[4] = null;\n\t\t\t\t\tro[5] = mr.getPriority();\n\t\t\t\t\tro[6] = btstring;\n\n\t\t\t\t\ttm.addRow(ro);\n\t\t\t\t\ttheTodoList.add(mr);\n\t\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\t// add open subtasks with a due date to the list\n\t\tif (Prefs.getBoolPref(PrefName.CAL_SHOW_SUBTASKS)) {\n\t\t\ttry {\n\t\t\t\tCollection<Subtask> sts = TaskModel.getReference()\n\t\t\t\t\t\t.getSubTasks();\n\t\t\t\tIterator<Subtask> it = sts.iterator();\n\t\t\t\twhile (it.hasNext()) {\n\n\t\t\t\t\tSubtask st = it.next();\n\n\t\t\t\t\tif (st.getDueDate() == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (st.getCloseDate() != null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tTask task = TaskModel.getReference().getTask(\n\t\t\t\t\t\t\tst.getTask().intValue());\n\t\t\t\t\tString cat = task.getCategory();\n\n\t\t\t\t\tif (!CategoryModel.getReference().isShown(cat))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// build a string for the table - with prefix if needed\n\t\t\t\t\tString abb = \"\";\n\t\t\t\t\tif (show_abb.equals(\"true\"))\n\t\t\t\t\t\tabb = \"ST\" + st.getKey() + \" \";\n\t\t\t\t\tString btstring = abb + st.getText();\n\n\t\t\t\t\tObject[] ro = new Object[7];\n\t\t\t\t\tro[0] = st.getDueDate();\n\t\t\t\t\tro[1] = btstring;\n\t\t\t\t\tro[2] = cat;\n\t\t\t\t\tif (st.getColor() == null)\n\t\t\t\t\t\tro[3] = \"black\";\n\t\t\t\t\telse\n\t\t\t\t\t\tro[3] = st.getColor();\n\t\t\t\t\tro[4] = null;\n\t\t\t\t\tro[5] = null;\n\t\t\t\t\tro[6] = btstring;\n\n\t\t\t\t\ttm.addRow(ro);\n\t\t\t\t\ttheTodoList.add(st);\n\t\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Errmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// sort the table by date\n\t\ttm.sortByColumn(0);\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\n\t\t// handle category change\n\t\tif (event.getModel() instanceof CategoryModel) {\n\t\t\tloadCategories();\n\t\t}\n\n\t\trefresh();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/WeekPanel.java",
    "content": "/*\n This file is part of BORG.\n \n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n \n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \n Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.BasicStroke;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GridBagConstraints;\nimport java.awt.Rectangle;\nimport java.awt.RenderingHints;\nimport java.awt.Shape;\nimport java.awt.Stroke;\nimport java.awt.Toolkit;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseWheelEvent;\nimport java.awt.event.MouseWheelListener;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.List;\n\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.PrintHelper;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Day;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.CalendarModule;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.NavPanel;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * WeekPanel is the UI for a single week. It consists of a Navigator attached to\n * a WeekSubPanel\n */\npublic class WeekPanel extends DockableView implements Printable,\n\t\tCalendarModule {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * WeekSubPanel is the Panel that shows the items for a week with a section\n\t * for untimed items and a time-grid for timed items\n\t */\n\tprivate class WeekSubPanel extends ApptBoxPanel implements\n\t\t\tNavPanel.Navigator, Prefs.Listener, Model.Listener, Printable,\n\t\t\tMouseWheelListener {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t// week start date\n\t\tprivate Date beg_ = null;\n\n\t\t// width of each day\n\t\tprivate double colwidth = 0;\n\n\t\t// set up dash line stroke\n\t\tprivate final float[] dash1 = { 1.0f, 3.0f };\n\t\tprivate final BasicStroke dashed = new BasicStroke(0.02f,\n\t\t\t\tBasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 3.0f, dash1, 0.0f);\n\n\t\t// date passed in - its week is the week to show\n\t\tprivate int date_;\n\t\tprivate int month_;\n\t\tprivate int year_;\n\n\t\t// portion of the screen taken up by the non-timed section\n\t\tprivate double nonTimedPortion = 1.0 / 6.0;\n\n\t\t// flag to indicate if we need to reload from the model. If false, we\n\t\t// can just redraw\n\t\t// from cached data\n\t\tprivate boolean needLoad = true;\n\n\t\t// records the last date on which a draw took place - for handling the\n\t\t// first redraw after midnight\n\t\tprivate int lastDrawDate = -1;\n\n\t\t// width of time column (where times are shown for y axis)\n\t\tprivate double timecolwidth = 0;\n\n\t\t// daily background colors\n\t\tprivate final Color[] backgroundColors = new Color[7];\n\n\t\t// zoom factor\n\t\tprivate int zoom = 0;\n\n\t\t/**\n\t\t * constructor\n\t\t * \n\t\t */\n\t\tpublic WeekSubPanel() {\n\n\t\t\t// react to pref changes\n\t\t\tPrefs.addListener(this);\n\n\t\t\t// react to mouse wheel changes\n\t\t\taddMouseWheelListener(this);\n\n\t\t\t// react to appt or task model changes\n\t\t\tAppointmentModel.getReference().addListener(this);\n\t\t\tTaskModel.getReference().addListener(this);\n\t\t\tAddressModel.getReference().addListener(this);\n\t\t\tSubscribedCalendars.getReference().addListener(this);\n\n\t\t\tgoTo(new GregorianCalendar());\n\n\t\t}\n\n\t\t/** clear data - will cause a reload on new redraw */\n\t\tpublic void clearData() {\n\t\t\tclearBoxes();\n\t\t\tneedLoad = true;\n\t\t\tsetToolTipText(null);\n\t\t}\n\n\t\t/**\n\t\t * draw the week into a Graphics\n\t\t */\n\t\tprivate int drawIt(Graphics g, double width, double height,\n\t\t\t\tdouble pageWidth, double pageHeight, double pagex,\n\t\t\t\tdouble pagey, Font sm_font) {\n\n\t\t\tTheme t = Theme.getCurrentTheme();\n\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,\n\t\t\t\t\tRenderingHints.VALUE_TEXT_ANTIALIAS_ON);\n\n\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, 0, (int) width, (int) height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// get font sizes\n\t\t\tint fontHeight = g2.getFontMetrics().getHeight();\n\t\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\n\t\t\t// bug fix if font metrics missing\n\t\t\tif( fontHeight == 0) fontHeight = 16;\n\t\t\tif( fontDesent == 0) fontDesent = 3;\n\t\t\t\n\t\t\t// translate coordinates to page margins\n\t\t\tg2.translate(pagex, pagey);\n\n\t\t\t// save original clip\n\t\t\tShape s = g2.getClip();\n\n\t\t\t// get current time\n\t\t\tGregorianCalendar now = new GregorianCalendar();\n\t\t\tint tdate = now.get(Calendar.DATE);\n\n\t\t\t// force reload if the date has changed since the last draw\n\t\t\tif (lastDrawDate != tdate) {\n\t\t\t\tneedLoad = true;\n\t\t\t}\n\t\t\tlastDrawDate = tdate;\n\n\t\t\t// set up calendar and determine first day of week from options\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tint fdow = Prefs.getIntPref(PrefName.FIRSTDOW);\n\t\t\tcal.setFirstDayOfWeek(fdow);\n\n\t\t\t// move cal back to first dow for the chosen week\n\t\t\tint offset = cal.get(Calendar.DAY_OF_WEEK) - fdow;\n\t\t\tif (offset == -1)\n\t\t\t\toffset = 6;\n\t\t\tcal.add(Calendar.DATE, -1 * offset);\n\n\t\t\t// save begin date\n\t\t\tbeg_ = cal.getTime();\n\n\t\t\t// top of drawn week - used to be non-zero to fit a title\n\t\t\tint caltop = 0;\n\n\t\t\t// top of untimed region - under day name\n\t\t\tint daytop = caltop + fontHeight + fontDesent;\n\n\t\t\t// height of box containing day's appts\n\t\t\tdouble rowheight = pageHeight - daytop;\n\n\t\t\t// width of column with time labels (Y axis)\n\t\t\ttimecolwidth = pageWidth / 21;\n\n\t\t\t// top of timed appts (the time-grid). untimed appts appear above\n\t\t\tdouble aptop = caltop + rowheight * nonTimedPortion;\n\n\t\t\t// width of each day column - related to timecolwidth\n\t\t\tcolwidth = (pageWidth - timecolwidth) / 7;\n\n\t\t\t// calculate the bottom edge of the grid\n\t\t\tint calbot = (int) rowheight + daytop;\n\n\t\t\t// limit resize (and dragging out of new items) to the time-grid\n\t\t\t// (timed appt area)\n\t\t\tsetResizeBounds((int) aptop, calbot);\n\n\t\t\t// allow dragging of items across the entire week - including both\n\t\t\t// timed and untimed areas)\n\t\t\tsetDragBounds(daytop, calbot, (int) timecolwidth, (int) (pageWidth));\n\n\t\t\t// start and end hour = range of Y axis\n\t\t\tString shr = Prefs.getPref(PrefName.WKSTARTHOUR);\n\t\t\tString ehr = Prefs.getPref(PrefName.WKENDHOUR);\n\t\t\tint starthr = 7;\n\t\t\tint endhr = 22;\n\t\t\ttry {\n\t\t\t\tstarthr = Integer.parseInt(shr);\n\t\t\t\tendhr = Integer.parseInt(ehr);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t\t// calculate size of Y-axis ticks (each half-hour)\n\t\t\tint numhalfhours = (endhr - starthr) * 2;\n\t\t\tdouble tickheight = (calbot - aptop) / numhalfhours;\n\n\t\t\t// format for day labels\n\t\t\tSimpleDateFormat dfw = new SimpleDateFormat(\"EEE dd\");\n\n\t\t\t// set time label column to default background\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, caltop, (int) timecolwidth, calbot - caltop);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// set small font for appt text\n\t\t\tg2.setFont(sm_font);\n\t\t\tint smfontHeight = g2.getFontMetrics().getHeight();\n\t\t\tif( smfontHeight == 0) smfontHeight = 7;\n\n\t\t\t// loop through the days of the week\n\t\t\tfor (int col = 0; col < 7; col++) {\n\n\t\t\t\t// position of the left of the day's column\n\t\t\t\tint colleft = (int) (timecolwidth + col * colwidth);\n\n\t\t\t\t// load data from model if needed\n\t\t\t\tif (needLoad) {\n\n\t\t\t\t\tCalendar today = new GregorianCalendar();\n\n\t\t\t\t\t// add a zone for this day\n\t\t\t\t\taddDateZone(cal.getTime(), new Rectangle(colleft, 0,\n\t\t\t\t\t\t\t(int) colwidth, calbot));\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstartmin = starthr * 60;\n\t\t\t\t\t\tendmin = endhr * 60;\n\n\t\t\t\t\t\t// get the day's items\n\t\t\t\t\t\tDay dayInfo = Day\n\t\t\t\t\t\t\t\t.getDay(cal.get(Calendar.YEAR),\n\t\t\t\t\t\t\t\t\t\tcal.get(Calendar.MONTH),\n\t\t\t\t\t\t\t\t\t\tcal.get(Calendar.DATE));\n\n\t\t\t\t\t\t// set a different background color based on various\n\t\t\t\t\t\t// circumstances\n\t\t\t\t\t\tColor backgroundColor = null;\n\t\t\t\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\n\t\t\t\t\t\tif (today.get(Calendar.MONTH) == month_\n\t\t\t\t\t\t\t\t&& today.get(Calendar.YEAR) == year_\n\t\t\t\t\t\t\t\t&& today.get(Calendar.DATE) == cal\n\t\t\t\t\t\t\t\t\t\t.get(Calendar.DATE)) {\n\t\t\t\t\t\t\t// day is today\n\t\t\t\t\t\t\tbackgroundColor = new Color(t.getTodayBg());\n\t\t\t\t\t\t} else if (dayInfo.getHoliday() != 0) {\n\t\t\t\t\t\t\t// holiday\n\t\t\t\t\t\t\tbackgroundColor = new Color(t.getHolidayBg());\n\t\t\t\t\t\t} else if (dayInfo.getVacation() == 1) {\n\t\t\t\t\t\t\t// full day vacation\n\t\t\t\t\t\t\tbackgroundColor = new Color(Theme.getCurrentTheme()\n\t\t\t\t\t\t\t\t\t.getVacationBg());\n\t\t\t\t\t\t} else if (dayInfo.getVacation() == 2) {\n\t\t\t\t\t\t\t// half-day vacation\n\t\t\t\t\t\t\tbackgroundColor = new Color(t.getHalfdayBg());\n\t\t\t\t\t\t} else if (dow == Calendar.SUNDAY\n\t\t\t\t\t\t\t\t|| dow == Calendar.SATURDAY) {\n\t\t\t\t\t\t\t// weekend\n\t\t\t\t\t\t\tbackgroundColor = new Color(t.getWeekendBg());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// weekday\n\t\t\t\t\t\t\tbackgroundColor = new Color(t.getWeekdayBg());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbackgroundColors[col] = backgroundColor;\n\n\t\t\t\t\t\t// determine Y coord for non-scheduled appts (notes)\n\t\t\t\t\t\t// they will be above the timed appt area\n\t\t\t\t\t\tint notey = daytop;// + smfontHeight;\n\n\t\t\t\t\t\t// loop through appts\n\t\t\t\t\t\tfor (CalendarEntity entity : dayInfo.getItems()) {\n\n\t\t\t\t\t\t\tDate d = entity.getDate();\n\n\t\t\t\t\t\t\t// sanity check - should not happen\n\t\t\t\t\t\t\tif (d == null)\n\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\t// determine appt start and end minutes\n\t\t\t\t\t\t\tGregorianCalendar acal = new GregorianCalendar();\n\t\t\t\t\t\t\tacal.setTime(d);\n\t\t\t\t\t\t\tdouble apstartmin = 60\n\t\t\t\t\t\t\t\t\t* acal.get(Calendar.HOUR_OF_DAY)\n\t\t\t\t\t\t\t\t\t+ acal.get(Calendar.MINUTE);\n\t\t\t\t\t\t\tint dur = 0;\n\t\t\t\t\t\t\tInteger duri = entity.getDuration();\n\t\t\t\t\t\t\tif (duri != null) {\n\t\t\t\t\t\t\t\tdur = duri.intValue();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdouble apendmin = apstartmin + dur;\n\n\t\t\t\t\t\t\t// check if the entity is a note (untime)\n\t\t\t\t\t\t\t// an entity can only be timed if it is an\n\t\t\t\t\t\t\t// appointment\n\t\t\t\t\t\t\t// that fits in the time-grid, has a duration, and\n\t\t\t\t\t\t\t// is timed\n\t\t\t\t\t\t\tif (!(entity instanceof Appointment)\n\t\t\t\t\t\t\t\t\t|| AppointmentModel\n\t\t\t\t\t\t\t\t\t\t\t.isNote((Appointment) entity)\n\t\t\t\t\t\t\t\t\t|| apendmin < startmin\n\t\t\t\t\t\t\t\t\t|| apstartmin >= endmin - 4\n\t\t\t\t\t\t\t\t\t|| entity.getDuration() == null\n\t\t\t\t\t\t\t\t\t|| entity.getDuration().intValue() == 0) {\n\n\t\t\t\t\t\t\t\tif (addNoteBox(cal.getTime(), entity,\n\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 2, notey,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) (colwidth - 4),\n\t\t\t\t\t\t\t\t\t\t\t\tsmfontHeight), new Rectangle(\n\t\t\t\t\t\t\t\t\t\t\t\tcolleft, caltop,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) colwidth,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) (aptop - caltop))) != null) {\n\t\t\t\t\t\t\t\t\t// increment Y coord for next note text\n\t\t\t\t\t\t\t\t\tnotey += smfontHeight;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// appt box bounds and clip are set to the day's\n\t\t\t\t\t\t\t\t// column in the grid.\n\t\t\t\t\t\t\t\t// will be laid out later\n\t\t\t\t\t\t\t\taddApptBox(cal.getTime(), (Appointment) entity,\n\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 4, (int) aptop,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) colwidth - 8,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) (calbot - aptop)),\n\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, (int) aptop,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) colwidth,\n\t\t\t\t\t\t\t\t\t\t\t\t(int) (calbot - aptop)));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t}\n\n\t\t\t\t\t// layout the timed items\n\t\t\t\t\tList<ApptBox> layoutlist = new ArrayList<ApptBox>();\n\t\t\t\t\tfor (Box b : boxes) {\n\t\t\t\t\t\tif (!(b instanceof ApptBox))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tApptBox ab = (ApptBox) b;\n\t\t\t\t\t\tCalendar c = new GregorianCalendar();\n\t\t\t\t\t\tc.setTime(ab.getDate());\n\t\t\t\t\t\tif (c.get(Calendar.DAY_OF_YEAR) == cal\n\t\t\t\t\t\t\t\t.get(Calendar.DAY_OF_YEAR))\n\t\t\t\t\t\t\tlayoutlist.add(ab);\n\t\t\t\t\t}\n\t\t\t\t\tApptBox.layoutBoxes(layoutlist, starthr, endhr);\n\n\t\t\t\t}\n\n\t\t\t\t// reset the clip or bad things happen\n\t\t\t\tg2.setClip(s);\n\n\t\t\t\t// adjust for rounding errors\n\t\t\t\tint nextColumnLeft = (int) (timecolwidth + (col + 1) * colwidth);\n\t\t\t\tint fixedWidth = nextColumnLeft - colleft;\n\t\t\t\tg2.setColor(this.getBackground());\n\t\t\t\tg2.fillRect(colleft, caltop, fixedWidth, daytop - caltop);\n\n\t\t\t\tg2.setColor(backgroundColors[col]);\n\t\t\t\tg2.fillRect(colleft, caltop, fixedWidth, calbot - caltop);\n\n\t\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t\t// increment the day\n\t\t\t\tcal.add(Calendar.DATE, 1);\n\n\t\t\t}\n\n\t\t\tneedLoad = false;\n\n\t\t\t// draw dashed lines for 1/2 hour intervals\n\t\t\tStroke defstroke = g2.getStroke();\n\t\t\tg2.setStroke(dashed);\n\t\t\tfor (int row = 0; row < numhalfhours; row++) {\n\t\t\t\tint rowtop = (int) ((row * tickheight) + aptop);\n\t\t\t\tg2.drawLine((int) timecolwidth, rowtop, (int) pageWidth, rowtop);\n\t\t\t}\n\t\t\tg2.setStroke(defstroke);\n\n\t\t\t// add the zoom buttons\n\t\t\tif (zoom < 4)\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/ZoomIn16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, caltop, 20, smfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tzoom++;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\n\t\t\tif (zoom > 0)\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/ZoomOut16.gif\"),\n\t\t\t\t\t\tnew Rectangle((int) timecolwidth - 20, caltop, 20,\n\t\t\t\t\t\t\t\tsmfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tzoom--;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\n\t\t\t// add the scroll buttons\n\t\t\tif (nonTimedPortion < 0.8) {\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/Down16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, (int) aptop, (int) timecolwidth,\n\t\t\t\t\t\t\t\tsmfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tnonTimedPortion += 1.0 / 6.0;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (nonTimedPortion > 0.2) {\n\t\t\t\tboxes.add(new ButtonBox(cal.getTime(), \"\", IconHelper.getIcon(\"/resource/Up16.gif\"),\n\t\t\t\t\t\tnew Rectangle(0, (int) aptop - smfontHeight,\n\t\t\t\t\t\t\t\t(int) timecolwidth, smfontHeight), null) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\tnonTimedPortion -= 1.0 / 6.0;\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// draw all boxes for the week\n\t\t\tdrawBoxes(g2);\n\t\t\t\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// reset calendar\n\t\t\tcal.setTime(beg_);\n\n\t\t\t// add the day labels\n\t\t\tfor (int col = 0; col < 7; col++) {\n\n\t\t\t\tint colleft = (int) (timecolwidth + (col * colwidth));\n\t\t\t\tString dayofweek = dfw.format(cal.getTime());\n\t\t\t\tint swidth = g2.getFontMetrics().stringWidth(dayofweek);\n\n\t\t\t\tg2.drawString(dayofweek,\n\t\t\t\t\t\t(int) (colleft + (colwidth - swidth) / 2), caltop\n\t\t\t\t\t\t\t\t+ fontHeight);\n\t\t\t\tcal.add(Calendar.DATE, 1);\n\t\t\t}\n\n\t\t\t// draw the box lines last so they show on top of other stuff\n\t\t\t// first - the horizontal lines\n\t\t\tg2.drawLine(0, caltop, (int) pageWidth, caltop);\n\t\t\tg2.drawLine(0, daytop, (int) pageWidth, daytop);\n\t\t\tg2.drawLine(0, (int) aptop, (int) pageWidth, (int) aptop);\n\t\t\tg2.drawLine(0, calbot, (int) pageWidth, calbot);\n\t\t\tg2.drawLine(0, caltop, 0, calbot);\n\n\t\t\t// draw the Y axis time labels\n\t\t\tg2.setFont(sm_font);\n\t\t\tboolean mt = false;\n\t\t\tString mil = Prefs.getPref(PrefName.MILTIME);\n\t\t\tif (mil.equals(\"true\"))\n\t\t\t\tmt = true;\n\t\t\tfor (int row = 1; row < endhr - starthr; row++) {\n\t\t\t\tint y = (int) ((row * tickheight * 2) + aptop);\n\t\t\t\tint hr = row + starthr;\n\n\t\t\t\tif (!mt && hr > 12)\n\t\t\t\t\thr = hr - 12;\n\n\t\t\t\tString tmlabel = hr + \":00\";\n\t\t\t\tg2.drawString(tmlabel, 2, y + smfontHeight / 2);\n\t\t\t}\n\n\t\t\t// the vertical lines\n\t\t\tfor (int col = 0; col < 8; col++) {\n\t\t\t\tint colleft = (int) (timecolwidth + (col * colwidth));\n\t\t\t\tg2.drawLine(colleft, caltop, colleft, calbot);\n\t\t\t}\n\n\t\t\t// to support printing\n\t\t\treturn Printable.PAGE_EXISTS;\n\t\t}\n\n\t\t/**\n\t\t * return date for a coordinate\n\t\t */\n\t\t@Override\n\t\tpublic Date getDateForCoord(double x, double y) {\n\n\t\t\t// determine the date based on what column we are in\n\t\t\tdouble col = (x - timecolwidth) / colwidth;\n\t\t\tif (col > 6)\n\t\t\t\tcol = 6;\n\t\t\tCalendar cal = new GregorianCalendar();\n\t\t\tcal.setTime(beg_);\n\t\t\tcal.add(Calendar.DATE, (int) col);\n\t\t\treturn cal.getTime();\n\t\t}\n\n\t\t/**\n\t\t * get nav label\n\t\t */\n\t\t@Override\n\t\tpublic String getNavLabel() {\n\n\t\t\t// set up calendar and determine first day of week from options\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tint fdow = Prefs.getIntPref(PrefName.FIRSTDOW);\n\t\t\tcal.setFirstDayOfWeek(fdow);\n\t\t\tif (Prefs.getPref(PrefName.ISOWKNUMBER).equals(\"true\"))\n\t\t\t\tcal.setMinimalDaysInFirstWeek(4);\n\t\t\telse\n\t\t\t\tcal.setMinimalDaysInFirstWeek(1);\n\n\t\t\t// move cal back to first dow for the chosen week\n\t\t\tint offset = cal.get(Calendar.DAY_OF_WEEK) - fdow;\n\t\t\tif (offset == -1)\n\t\t\t\toffset = 6;\n\t\t\tcal.add(Calendar.DATE, -1 * offset);\n\n\t\t\t// save begin/end date and build title\n\t\t\tDate beg = cal.getTime();\n\t\t\tcal.add(Calendar.DATE, 6);\n\t\t\tDate end = cal.getTime();\n\t\t\tDateFormat df = DateFormat.getDateInstance(DateFormat.LONG);\n\n\t\t\treturn df.format(beg) + \" \"\n\t\t\t\t\t+ Resource.getResourceString(\"__through__\") + \" \"\n\t\t\t\t\t+ df.format(end) + \"     (\" + cal.get(Calendar.WEEK_OF_YEAR) + \")\";\n\n\t\t}\n\n\t\t/**\n\t\t * go to a particular date\n\t\t */\n\t\t@Override\n\t\tpublic void goTo(Calendar cal) {\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * navigate forward or back based on mouse wheel action\n\t\t */\n\t\t@Override\n\t\tpublic void mouseWheelMoved(MouseWheelEvent e) {\n\t\t\tif (e.getWheelRotation() > 0) {\n\t\t\t\tnext();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t} else if (e.getWheelRotation() < 0) {\n\t\t\t\tprev();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * navigate forward 1 week\n\t\t */\n\t\t@Override\n\t\tpublic void next() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tcal.add(Calendar.DATE, 7);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t\ttry {\n\t\t\t\tFont sm_font = Font\n\t\t\t\t\t\t.decode(Prefs.getPref(PrefName.WEEKVIEWFONT));\n\t\t\t\tdrawIt(g, getWidth(), getHeight(), getWidth() - 20,\n\t\t\t\t\t\tgetHeight() - 20, 10, 10, sm_font);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw if prefs change\n\t\t */\n\t\t@Override\n\t\tpublic void prefsChanged() {\n\t\t\tclearData();\n\t\t\trepaint();\n\n\t\t}\n\n\t\t/**\n\t\t * navigate backwards 1 week\n\t\t */\n\t\t@Override\n\t\tpublic void prev() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, month_, date_,\n\t\t\t\t\t23, 59);\n\t\t\tcal.add(Calendar.DATE, -7);\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\n\t\t/**\n\t\t * print to a Graphics for a printout\n\t\t */\n\t\t@Override\n\t\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\t\tthrows PrinterException {\n\t\t\t// only print 1 page\n\t\t\tif (pageIndex > 0)\n\t\t\t\treturn Printable.NO_SUCH_PAGE;\n\t\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.PRINTFONT));\n\t\t\tclearData();\n\t\t\tint ret = drawIt(g, pageFormat.getWidth(), pageFormat.getHeight(),\n\t\t\t\t\tpageFormat.getImageableWidth(),\n\t\t\t\t\tpageFormat.getImageableHeight(),\n\t\t\t\t\tpageFormat.getImageableX(), pageFormat.getImageableY(),\n\t\t\t\t\tsm_font);\n\t\t\trefresh();\n\t\t\treturn ret;\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw\n\t\t */\n\t\t@Override\n\t\tpublic void refresh() {\n\t\t\tToolkit toolkit = Toolkit.getDefaultToolkit();\n\t\t\tDimension dim = toolkit.getScreenSize();\n\t\t\tint h = ((zoom + 2) * (int) dim.getHeight()) / 2;\n\t\t\tif (zoom == 0)\n\t\t\t\th = 0;\n\t\t\tthis.setPreferredSize(new Dimension(0, h));\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tthis.getParent().doLayout();\n\t\t}\n\n\t\t@Override\n\t\tpublic void update(ChangeEvent event) {\n\t\t\trefresh();\n\t\t}\n\n\t\t/**\n\t\t * navigate to the current week\n\t\t */\n\t\t@Override\n\t\tpublic void today() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tmonth_ = cal.get(Calendar.MONTH);\n\t\t\tdate_ = cal.get(Calendar.DATE);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t\tMultiView.getMainView().goTo(cal);\n\n\t\t}\n\t}\n\n\t// the navigator panel\n\tprivate NavPanel nav = null;\n\n\t// the panel to draw the week\n\tprivate WeekSubPanel wp_ = null;\n\t\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t * \n\t */\n\tpublic WeekPanel() {\n\n\t\t\n\t}\n\n\t/**\n\t * go to a particular week\n\t * \n\t * @param cal\n\t *            a day in the week\n\t */\n\t@Override\n\tpublic void goTo(Calendar cal) {\n\t\tif( isInitialized ){\n\t\t\twp_.goTo(cal);\n\t\t\tnav.setLabel(wp_.getNavLabel());\n\t\t}\n\t}\n\n\t/**\n\t * print the UI\n\t */\n\t@Override\n\tpublic int print(Graphics arg0, PageFormat arg1, int arg2)\n\t\t\tthrows PrinterException {\n\t\treturn wp_.print(arg0, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Week_View\");\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\t\t\n\t\tif( !isInitialized)\n\t\t{\n\t\t\t// create the week container panel\n\t\t\twp_ = new WeekSubPanel();\n\n\t\t\t// create the navigator\n\t\t\tnav = new NavPanel(wp_);\n\n\t\t\tJScrollPane sp = new JScrollPane();\n\t\t\tsp.setViewportView(wp_);\n\n\t\t\tsetLayout(new java.awt.GridBagLayout());\n\t\t\tadd(nav,\n\t\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\t\t\tadd(sp, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH,\n\t\t\t\t\t1.0, 1.0));\n\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/week.jpg\"),\n\t\t\t\tgetModuleName(), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(ViewType.WEEK);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.WEEK);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tPrintHelper.printPrintable(this);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.WEEK;\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\t// do nothing - children do their own refresh\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// do nothing - children do their own refresh\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/YearPanel.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.calendar;\n\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.GridBagConstraints;\nimport java.awt.Rectangle;\nimport java.awt.RenderingHints;\nimport java.awt.Shape;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseWheelEvent;\nimport java.awt.event.MouseWheelListener;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\nimport javax.swing.JPanel;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.PrintHelper;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.Day;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.model.entity.CalendarEntity;\nimport net.sf.borg.model.sync.SubscribedCalendars;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.CalendarModule;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.NavPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * YearPanel is the Year UI. It shows all days of the year and allows navigation\n * to months, days, and weeks. It gives indications of the presence of\n * appointments, but not individual appt details\n */\n\npublic class YearPanel extends DockableView implements Printable,\n\t\tCalendarModule {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * YearViewSubPanel is the panel that draws the year UI\n\t */\n\tprivate class YearViewSubPanel extends ApptBoxPanel implements Printable,\n\t\t\tNavPanel.Navigator, Model.Listener, Prefs.Listener,\n\t\t\tMouseWheelListener {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t// number of day boxes - 37 per month. not all are used.\n\t\tstatic final private int numBoxes = 37 * 12;\n\n\t\t// cached day box colors\n\t\tColor[] colors = new Color[numBoxes];\n\n\t\t// width of a day column\n\t\tprivate int colwidth;\n\n\t\t// top of year UI - under the day of week labels\n\t\tprivate int gridtop;\n\n\t\t// records the last date on which a draw took place - for handling the\n\t\t// first redraw after midnight\n\t\tprivate int lastDrawDate = -1;\n\n\t\t// flag to indicate if we need to reload from the model. If false, we\n\t\t// can just redraw\n\t\t// from cached data\n\t\tboolean needLoad = true;\n\n\t\t// height of a month row\n\t\tprivate int rowheight;\n\n\t\t// year being shown\n\t\tprivate int year_;\n\n\t\t/**\n\t\t * constructor\n\t\t */\n\t\tpublic YearViewSubPanel() {\n\n\t\t\t// react to pref changes\n\t\t\tPrefs.addListener(this);\n\n\t\t\t// react to mouse wheel actions\n\t\t\taddMouseWheelListener(this);\n\n\t\t\t// react to appointment mode changes\n\t\t\tAppointmentModel.getReference().addListener(this);\n\t\t\tSubscribedCalendars.getReference().addListener(this);\n\n\n\t\t\tgoTo(new GregorianCalendar());\n\t\t}\n\n\t\t/**\n\t\t * clear cached data - will reload on next draw\n\t\t */\n\t\tpublic void clearData() {\n\t\t\tclearBoxes();\n\t\t\tneedLoad = true;\n\t\t\tsetToolTipText(null);\n\t\t}\n\n\t\t/**\n\t\t * draw the year UI\n\t\t */\n\t\tprivate int drawIt(Graphics g, double width, double height,\n\t\t\t\tdouble pageWidth, double pageHeight, double pagex,\n\t\t\t\tdouble pagey, Font sm_font) {\n\n\t\t\tTheme t = Theme.getCurrentTheme();\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,\n\t\t\t\t\tRenderingHints.VALUE_TEXT_ANTIALIAS_ON);\n\t\t\tg2.setFont(sm_font);\n\n\t\t\t// draw a white background\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, 0, (int) width, (int) height);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// get font sizes\n\t\t\tint fontHeight = g2.getFontMetrics().getHeight();\n\t\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\n\t\t\t// translate coordinates based on page margins\n\t\t\tg2.translate(pagex, pagey);\n\n\t\t\tShape s = g2.getClip();\n\n\t\t\t// set current date\n\t\t\tGregorianCalendar now = new GregorianCalendar();\n\t\t\tint tmon = now.get(Calendar.MONTH);\n\t\t\tint tyear = now.get(Calendar.YEAR);\n\t\t\tint tdate = now.get(Calendar.DATE);\n\n\t\t\t// force reload if the date has changed since the last draw\n\t\t\tif (lastDrawDate != tdate) {\n\t\t\t\tneedLoad = true;\n\t\t\t}\n\t\t\tlastDrawDate = tdate;\n\n\t\t\t// set date to first day of year and set first day of week according\n\t\t\t// to prefs\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_,\n\t\t\t\t\tCalendar.JANUARY, 1);\n\t\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\n\t\t\t// top of drawn year - used to be non-zero to fit a title\n\t\t\tint caltop = 0;\n\n\t\t\t// grid starts under the day of week labels\n\t\t\tgridtop = caltop + fontHeight + fontDesent;\n\n\t\t\t// width of month column\n\t\t\tint monthwidth = g2.getFontMetrics().stringWidth(\"12345678901\");\n\n\t\t\t// calculate width and height of day boxes\n\t\t\trowheight = ((int) pageHeight - gridtop) / 12;\n\t\t\tcolwidth = (int) (pageWidth - monthwidth) / 37;\n\n\t\t\t// ui right and bottom\n\t\t\tint calright = monthwidth + colwidth * 37;\n\t\t\tint calbot = gridtop + 12 * rowheight;\n\n\t\t\tsetDragBounds(gridtop, calbot, monthwidth, calright);\n\n\t\t\t// do not allow anything to be resized\n\t\t\tsetResizeBounds(0, 0);\n\n\t\t\t// background for day of week labels\n\t\t\tg2.setColor(new Color(t.getDefaultBg()));\n\t\t\tg2.fillRect(0, caltop, calright, gridtop - caltop);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// month name format\n\t\t\tSimpleDateFormat dfm = new SimpleDateFormat(\"MMMM\");\n\n\t\t\t// draw the day of week names centered in each column - no boxes\n\t\t\t// drawn yet\n\t\t\tSimpleDateFormat dfw = new SimpleDateFormat(\"E\");\n\t\t\tcal.add(Calendar.DAY_OF_WEEK,\n\t\t\t\t\tcal.getFirstDayOfWeek() - cal.get(Calendar.DAY_OF_WEEK));\n\t\t\tfor (int col = 0; col < 37; col++) {\n\t\t\t\tint colleft = monthwidth + (col * colwidth);\n\t\t\t\tString dayofweek = dfw.format(cal.getTime());\n\t\t\t\tdayofweek = dayofweek.substring(0, 1);\n\t\t\t\tint swidth = g2.getFontMetrics().stringWidth(dayofweek);\n\t\t\t\tg2.drawString(dayofweek, colleft + (colwidth - swidth) / 2,\n\t\t\t\t\t\tcaltop + fontHeight);\n\t\t\t\tcal.add(Calendar.DAY_OF_WEEK, 1);\n\t\t\t}\n\n\t\t\t// draw the days of the year. loop by month\n\t\t\tint mon = Calendar.JANUARY;\n\t\t\tfor (int row = 0; row < 12; row++) {\n\n\t\t\t\t// start with first of month\n\t\t\t\tcal.set(year_, mon, 1);\n\n\t\t\t\t// back up calendar to first day of week\n\t\t\t\tint fdow = cal.get(Calendar.DAY_OF_WEEK)\n\t\t\t\t\t\t- cal.getFirstDayOfWeek();\n\t\t\t\tif (fdow == -1)\n\t\t\t\t\tfdow = 6;\n\t\t\t\tcal.add(Calendar.DATE, -1 * fdow);\n\n\t\t\t\t// print the days\n\t\t\t\tfor (int col = 0; col < 37; col++) {\n\n\t\t\t\t\tint numbox = row * 37 + col;\n\t\t\t\t\tint rowtop = (row * rowheight) + gridtop;\n\t\t\t\t\tint colleft = monthwidth + (col * colwidth);\n\t\t\t\t\tint dow = cal.get(Calendar.DAY_OF_WEEK);\n\n\t\t\t\t\t// set clip to the day box to truncate long appointment text\n\t\t\t\t\tg2.clipRect(colleft, rowtop, colwidth, rowheight);\n\n\t\t\t\t\t// load from model if needed\n\t\t\t\t\tif (needLoad) {\n\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t// if day is not in the month for this row - then\n\t\t\t\t\t\t\t// use the default background\n\t\t\t\t\t\t\t// and don't add any ui components - will be an\n\t\t\t\t\t\t\t// empty box\n\t\t\t\t\t\t\tif (cal.get(Calendar.MONTH) != mon) {\n\t\t\t\t\t\t\t\tcolors[numbox] = new Color(t.getDefaultBg());\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// add the month button with the month name.\n\t\t\t\t\t\t\t\t// pressing it will navigate to the month view\n\t\t\t\t\t\t\t\tif (cal.get(Calendar.DATE) == 1) {\n\t\t\t\t\t\t\t\t\tboxes.add(new ButtonBox(\n\t\t\t\t\t\t\t\t\t\t\tcal.getTime(),\n\t\t\t\t\t\t\t\t\t\t\tdfm.format(cal.getTime()),\n\t\t\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t\t\tnew Rectangle(2, rowtop,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmonthwidth - 4, fontHeight),\n\t\t\t\t\t\t\t\t\t\t\tnew Rectangle(0, rowtop,\n\t\t\t\t\t\t\t\t\t\t\t\t\tmonthwidth, rowheight)) {\n\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\t\t\t\t\t\tMultiView.getMainView().setView(\n\t\t\t\t\t\t\t\t\t\t\t\t\tViewType.MONTH);\n\t\t\t\t\t\t\t\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\t\t\t\t\t\t\t\tgc.setTime(getDate());\n\t\t\t\t\t\t\t\t\t\t\tMultiView.getMainView().goTo(gc);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// get the appointment info for the given day\n\t\t\t\t\t\t\t\tDay dayInfo = Day.getDay(\n\t\t\t\t\t\t\t\t\t\tcal.get(Calendar.YEAR),\n\t\t\t\t\t\t\t\t\t\tcal.get(Calendar.MONTH),\n\t\t\t\t\t\t\t\t\t\tcal.get(Calendar.DATE));\n\n\t\t\t\t\t\t\t\t// default box color\n\t\t\t\t\t\t\t\tColor c = new Color(t.getWeekdayBg());\n\n\t\t\t\t\t\t\t\t// if there are entities for the day...\n\t\t\t\t\t\t\t\tif (dayInfo != null) {\n\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t// set the day box background color based on\n\t\t\t\t\t\t\t\t\t// various items\n\t\t\t\t\t\t\t\t\tif (tmon == cal.get(Calendar.MONTH)\n\t\t\t\t\t\t\t\t\t\t\t&& tyear == year_\n\t\t\t\t\t\t\t\t\t\t\t&& tdate == cal.get(Calendar.DATE)) {\n\t\t\t\t\t\t\t\t\t\tc = new Color(t.getTodayBg());\n\t\t\t\t\t\t\t\t\t} else if (dayInfo.getHoliday() != 0) {\n\t\t\t\t\t\t\t\t\t\tc = new Color(t.getHolidayBg());\n\t\t\t\t\t\t\t\t\t} else if (dayInfo.getVacation() == 1) {\n\t\t\t\t\t\t\t\t\t\tc = new Color(Theme.getCurrentTheme()\n\t\t\t\t\t\t\t\t\t\t\t\t.getVacationBg());\n\t\t\t\t\t\t\t\t\t} else if (dayInfo.getVacation() == 2) {\n\t\t\t\t\t\t\t\t\t\tc = new Color(t.getHalfdayBg());\n\t\t\t\t\t\t\t\t\t} else if (dow == Calendar.SUNDAY\n\t\t\t\t\t\t\t\t\t\t\t|| dow == Calendar.SATURDAY) {\n\t\t\t\t\t\t\t\t\t\tc = new Color(t.getWeekendBg());\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tc = new Color(t.getWeekdayBg());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// save the box color\n\t\t\t\t\t\t\t\tcolors[numbox] = c;\n\n\t\t\t\t\t\t\t\t// set small font for appt text\n\t\t\t\t\t\t\t\tg2.setFont(sm_font);\n\t\t\t\t\t\t\t\tint smfontHeight = g2.getFontMetrics()\n\t\t\t\t\t\t\t\t\t\t.getHeight();\n\n\t\t\t\t\t\t\t\t// set initial Y coord for item text\n\t\t\t\t\t\t\t\tint notey = rowtop + smfontHeight;\n\n\t\t\t\t\t\t\t\t// loop through entities for the day\n\t\t\t\t\t\t\t\tif (dayInfo != null) {\n\t\t\t\t\t\t\t\t\tfor (CalendarEntity entity : dayInfo\n\t\t\t\t\t\t\t\t\t\t\t.getItems()) {\n\n\t\t\t\t\t\t\t\t\t\t// add the item NoteBox to the container\n\t\t\t\t\t\t\t\t\t\tif (addNoteBox(cal.getTime(), entity,\n\t\t\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft + 2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnotey, colwidth - 4,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsmfontHeight),\n\t\t\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, rowtop,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolwidth, rowheight)) != null)\n\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t// increment Y coord for next note\n\t\t\t\t\t\t\t\t\t\t\t// text\n\t\t\t\t\t\t\t\t\t\t\tnotey += smfontHeight;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// get the date String\n\t\t\t\t\t\t\t\tString datetext = Integer.toString(cal\n\t\t\t\t\t\t\t\t\t\t.get(Calendar.DATE));\n\n\t\t\t\t\t\t\t\t// add the date button - clicking it goes to the\n\t\t\t\t\t\t\t\t// day view\n\t\t\t\t\t\t\t\tboxes.add(new ButtonBox(cal.getTime(),\n\t\t\t\t\t\t\t\t\t\tdatetext, null, new Rectangle(\n\t\t\t\t\t\t\t\t\t\t\t\tcolleft + 2, rowtop,\n\t\t\t\t\t\t\t\t\t\t\t\tcolwidth - 4, fontHeight),\n\t\t\t\t\t\t\t\t\t\tnew Rectangle(colleft, rowtop,\n\t\t\t\t\t\t\t\t\t\t\t\tcolwidth, rowheight), c) {\n\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\tpublic void onClick() {\n\t\t\t\t\t\t\t\t\t\tMultiView.getMainView().setView(\n\t\t\t\t\t\t\t\t\t\t\t\tViewType.DAY);\n\t\t\t\t\t\t\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\t\t\t\t\t\t\tgc.setTime(getDate());\n\t\t\t\t\t\t\t\t\t\tMultiView.getMainView().goTo(gc);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// reset the clip or bad things happen\n\t\t\t\t\tg2.setClip(s);\n\n\t\t\t\t\t// draw the current box's background\n\t\t\t\t\tg2.setColor(colors[numbox]);\n\t\t\t\t\tg2.fillRect(colleft, rowtop, colwidth, rowheight);\n\n\t\t\t\t\t// increment the day\n\t\t\t\t\tcal.add(Calendar.DATE, 1);\n\t\t\t\t}\n\n\t\t\t\tmon++;\n\n\t\t\t}\n\n\t\t\tneedLoad = false;\n\n\t\t\t// draw all of the box items (i.e. buttons)\n\t\t\tdrawBoxes(g2);\n\t\t\tg2.setClip(s);\n\t\t\tg2.setColor(new Color(t.getDefaultFg()));\n\n\t\t\t// draw the lines last\n\t\t\t// top of calendar - above day names\n\t\t\tg2.drawLine(0, caltop, calright, caltop);\n\n\t\t\t// left border\n\t\t\tg2.drawLine(0, caltop, 0, calbot);\n\n\t\t\t// horizontal\n\t\t\tfor (int row = 0; row <= 12; row++) {\n\t\t\t\tint rowtop = (row * rowheight) + gridtop;\n\t\t\t\tg2.drawLine(0, rowtop, calright, rowtop);\n\t\t\t}\n\n\t\t\t// vertical\n\t\t\tfor (int col = 0; col <= 37; col++) {\n\t\t\t\tint colleft = monthwidth + (col * colwidth);\n\t\t\t\tg2.drawLine(colleft, caltop, colleft, calbot);\n\t\t\t}\n\n\t\t\treturn Printable.PAGE_EXISTS;\n\t\t}\n\n\t\t/**\n\t\t * get date for given x/y coord\n\t\t */\n\t\t@Override\n\t\tpublic Date getDateForCoord(double x, double y) {\n\n\t\t\tint col = (int) x / colwidth;\n\t\t\tint row = ((int) y - gridtop) / rowheight;\n\t\t\tGregorianCalendar cal = new GregorianCalendar(year_, row, 1);\n\t\t\tcal.setFirstDayOfWeek(Prefs.getIntPref(PrefName.FIRSTDOW));\n\t\t\tint fdow = cal.get(Calendar.DAY_OF_WEEK) - cal.getFirstDayOfWeek();\n\t\t\tcal.add(Calendar.DATE, -1 * fdow);\n\t\t\tcal.add(Calendar.DATE,  col - 1);\n\t\t\treturn cal.getTime();\n\t\t}\n\n\t\t/**\n\t\t * get navigator label\n\t\t */\n\t\t@Override\n\t\tpublic String getNavLabel() {\n\t\t\tSimpleDateFormat df = new SimpleDateFormat(\"yyyy\");\n\t\t\tCalendar cal = new GregorianCalendar(year_, Calendar.JANUARY, 1);\n\t\t\treturn df.format(cal.getTime());\n\t\t}\n\n\t\t/**\n\t\t * goto a particular year\n\t\t */\n\t\t@Override\n\t\tpublic void goTo(Calendar cal) {\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * go forward or back 1 year based on mouse wheel\n\t\t */\n\t\t@Override\n\t\tpublic void mouseWheelMoved(MouseWheelEvent e) {\n\t\t\tif (e.getWheelRotation() > 0) {\n\t\t\t\tnext();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t} else if (e.getWheelRotation() < 0) {\n\t\t\t\tprev();\n\t\t\t\tnav.setLabel(getNavLabel());\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * navigate forward 1 year\n\t\t */\n\t\t@Override\n\t\tpublic void next() {\n\t\t\tyear_++;\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t@Override\n\t\tprotected void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t\ttry {\n\t\t\t\tFont sm_font = Font\n\t\t\t\t\t\t.decode(Prefs.getPref(PrefName.YEARVIEWFONT));\n\t\t\t\tdrawIt(g, getWidth(), getHeight(), getWidth() - 20,\n\t\t\t\t\t\tgetHeight() - 20, 10, 10, sm_font);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Errmsg.getErrorHandler().errmsg(e);\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * react to prefs changed - reload and redraw\n\t\t */\n\t\t@Override\n\t\tpublic void prefsChanged() {\n\t\t\tclearData();\n\t\t\trepaint();\n\n\t\t}\n\n\t\t/**\n\t\t * navigate back 1 year\n\t\t */\n\t\t@Override\n\t\tpublic void prev() {\n\t\t\tyear_--;\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t/**\n\t\t * draw the UI for printing\n\t\t */\n\t\t@Override\n\t\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\t\tthrows PrinterException {\n\n\t\t\tif (pageIndex > 0)\n\t\t\t\treturn Printable.NO_SUCH_PAGE;\n\t\t\tFont sm_font = Font.decode(Prefs.getPref(PrefName.PRINTFONT));\n\t\t\tclearData();\n\t\t\tint ret = drawIt(g, pageFormat.getWidth(), pageFormat.getHeight(),\n\t\t\t\t\tpageFormat.getImageableWidth(),\n\t\t\t\t\tpageFormat.getImageableHeight(),\n\t\t\t\t\tpageFormat.getImageableX(), pageFormat.getImageableY(),\n\t\t\t\t\tsm_font);\n\t\t\trefresh();\n\t\t\treturn ret;\n\t\t}\n\n\t\t/**\n\t\t * reload and redraw\n\t\t */\n\t\t@Override\n\t\tpublic void refresh() {\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t\t@Override\n\t\tpublic void update(ChangeEvent event) {\n\t\t\trefresh();\n\t\t}\n\n\t\t/**\n\t\t * navigate to the current year\n\t\t */\n\t\t@Override\n\t\tpublic void today() {\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tyear_ = cal.get(Calendar.YEAR);\n\t\t\tclearData();\n\t\t\trepaint();\n\t\t}\n\n\t}\n\n\t// navigator panel\n\tprivate NavPanel nav = null;\n\n\t// year UI panel\n\tprivate YearViewSubPanel yearPanel = null;\n\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * constructor\n\t */\n\tpublic YearPanel() {\n\n\t\t\n\t}\n\n\t/**\n\t * goto a particular year\n\t */\n\t@Override\n\tpublic void goTo(Calendar cal) {\n\t\tif( isInitialized ){\n\t\t\tyearPanel.goTo(cal);\n\t\t\tnav.setLabel(yearPanel.getNavLabel());\n\t\t}\n\t}\n\n\t/**\n\t * draw for printing\n\t */\n\t@Override\n\tpublic int print(Graphics arg0, PageFormat arg1, int arg2)\n\t\t\tthrows PrinterException {\n\t\treturn yearPanel.print(arg0, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Year_View\");\n\t}\n\n\t@Override\n\tpublic JPanel getComponent() {\n\t\t\n\t\tif( !isInitialized )\n\t\t{\n\t\t\t// create the year ui\n\t\t\tyearPanel = new YearViewSubPanel();\n\t\t\t// create the nav panel\n\t\t\tnav = new NavPanel(yearPanel);\n\n\t\t\tsetLayout(new java.awt.GridBagLayout());\n\t\t\tadd(nav,\n\t\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\t\t\tadd(yearPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/year.jpg\"),\n\t\t\t\tgetModuleName(), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(ViewType.YEAR);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tPrintHelper.printPrintable(this);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.YEAR;\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\t// do nothing - children do their own refresh\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// do nothing - children do their own refresh\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/calendar/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  the calendar package contains UI classes for the calendar views and appointments\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/checklist/CheckListPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.checklist;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.Collection;\nimport java.util.Iterator;\n\nimport javax.swing.JButton;\nimport javax.swing.JComponent;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.event.ListSelectionListener;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.event.TableModelListener;\nimport javax.swing.table.TableCellRenderer;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.CheckListModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.entity.CheckList;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.InputDialog;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * UI for editing checkLists. It has a table that shows all checkLists by name\n * and an editing panel for editing checkList text.\n */\npublic class CheckListPanel extends DockableView implements\n\t\tListSelectionListener, Module, TableModelListener {\n\n\tprivate TableCellRenderer defaultTextRenderer = null;\n\n\t/**\n\t * renders the item text in strike-through if the item is completed *\n\t */\n\tprivate class ItemTextRenderer extends JLabel implements TableCellRenderer {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tpublic ItemTextRenderer() {\n\t\t\tsuper();\n\t\t\t//setOpaque(true); // MUST do this for background to show up.\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see\n\t\t * javax.swing.table.TableCellRenderer#getTableCellRendererComponent\n\t\t * (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)\n\t\t */\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table,\n\t\t\t\tObject obj, boolean isSelected, boolean hasFocus, int row,\n\t\t\t\tint column) {\n\n\t\t\tJLabel l = (JLabel) defaultTextRenderer\n\t\t\t\t\t.getTableCellRendererComponent(table, obj, isSelected,\n\t\t\t\t\t\t\thasFocus, row, column);\n\t\t\tthis.setHorizontalAlignment(l.getHorizontalAlignment());\n\t\t\tthis.setForeground(l.getForeground());\n\t\t\tthis.setBackground(l.getBackground());\n\n\t\t\tString text = l.getText();\n\t\t\tBoolean complete = (Boolean) table.getModel().getValueAt(row,\n\t\t\t\t\tCOMPLETE_COLUMN);\n\t\t\tif (complete == Boolean.TRUE) {\n\t\t\t\ttext = \"<HTML><STRIKE>\" + text + \"</STRIKE></HTML>\";\n\t\t\t}\n\n\t\t\tthis.setText(text);\n\t\t\treturn this;\n\n\t\t}\n\t}\n\n\t// table columns\n\t/** The Constant COMPLETE_COLUMN. */\n\tstatic private final int COMPLETE_COLUMN = 0;\n\n\t/** The Constant serialVersionUID. */\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The Constant TEXT_COLUMN. */\n\tstatic private final int TEXT_COLUMN = 1;\n\n\t/** The checkList list table. */\n\tprivate JTable checkListListTable = null;\n\n\t/** The edited checkList index. */\n\tprivate int editedCheckListIndex = -1;\n\n\t/** is checkList changed flag. */\n\tprivate boolean isCheckListEdited = false;\n\n\t/** The table of checklist items. */\n\tprivate JTable itemTable = null;\n\n\tprivate boolean isInitialized = false;\n\n\tprivate final ActionListener renameAction = new ActionListener() {\n\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent e) {\n\n\t\t\tString name = getSelectedCheckListName();\n\t\t\tif (name == null) {\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Select_CheckList_Warning\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (isCheckListEdited) {\n\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\tResource.getResourceString(\"Edited_CheckList\"),\n\t\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (itemTable.isEditing())\n\t\t\t\titemTable.getCellEditor().stopCellEditing();\n\n\t\t\tisCheckListEdited = false;\n\n\t\t\tString newname = InputDialog.show(Resource\n\t\t\t\t\t.getResourceString(\"Enter_CheckList_Name\"), 50);\n\n\t\t\tif (newname == null || newname.isEmpty() || newname.equals(name))\n\t\t\t\treturn;\n\n\t\t\tif (newname.length() > 50) {\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"max_length\", new String[] {\n\t\t\t\t\t\t\t\tResource.getResourceString(\"CheckList_Name\"),\n\t\t\t\t\t\t\t\t\"50\" }));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tCheckList existing = CheckListModel.getReference().getCheckList(newname);\n\t\t\t\tif (existing != null) {\n\t\t\t\t\t// name already used\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"Existing_CheckList\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\n\t\t\tCheckList m;\n\t\t\ttry {\n\t\t\t\tm = CheckListModel.getReference().getCheckList(name);\n\t\t\t\tm.setCheckListName(newname);\n\t\t\t\tCheckListModel.getReference().saveCheckList(m);\n\t\t\t\tCheckListModel.getReference().delete(name, false);\n\t\t\t\tloadCheckListsFromModel();\n\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\n\t\t}\n\n\t};\n\n\t/**\n\t * constructor.\n\t */\n\tpublic CheckListPanel() {\n\t\tsuper();\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * net.sf.borg.ui.util.JTabbedPaneWithCloseIcons.TabCloseListener#canClose()\n\t */\n\t@Override\n\tpublic boolean canClose() {\n\t\tif (isCheckListEdited) {\n\t\t\t/*\n\t\t\t * confirm discard of changes\n\t\t\t */\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_CheckList\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\n\t\t\t// if user does not want to lose changes, we need to set\n\t\t\t// the\n\t\t\t// selection back to the edited checkList\n\t\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tisCheckListEdited = false;\n\t\t\tthis.loadCheckListsFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete the selected checkList form the model.\n\t */\n\tprivate void deleteCheckList() {\n\t\tString name = getSelectedCheckListName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_CheckList_Warning\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// confirm delete\n\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\tResource.getResourceString(\"Really_Delete_\") + \"?\",\n\t\t\t\tResource.getResourceString(\"Confirm_Delete\"),\n\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tCheckListModel.getReference().delete(name, false);\n\t\t\tisCheckListEdited = false;\n\t\t\tloadCheckListsFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Copy the selected checkList\n\t */\n\tprivate void copyCheckList() {\n\t\tString name = getSelectedCheckListName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_CheckList_Warning\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// if the user is currently editing another checkList, confirm that we\n\t\t// should discard changes\n\t\tif (this.isCheckListEdited) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Cannot_Copy\"));\n\t\t\treturn;\n\t\t\t// int ret = JOptionPane.showConfirmDialog(null,\n\t\t\t// Resource.getResourceString(\"Edited_CheckList\"),\n\t\t\t// Resource.getResourceString(\"Discard_Text?\"),\n\t\t\t// JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\t\t// if (ret != JOptionPane.OK_OPTION)\n\t\t\t// return;\n\t\t}\n\n\t\tString newname = InputDialog.show(Resource\n\t\t\t\t.getResourceString(\"Enter_CheckList_Name\"), 50);\n\n\t\tif (newname == null || newname.trim().isEmpty())\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tCheckList existing = CheckListModel.getReference().getCheckList(\n\t\t\t\t\tnewname);\n\t\t\tif (existing != null) {\n\t\t\t\t// checkList name already used\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Existing_CheckList\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t}\n\n\t\ttry {\n\t\t\tCheckList orig = CheckListModel.getReference().getCheckList(name);\n\t\t\tCheckList copy = orig.clone();\n\t\t\tcopy.setCheckListName(newname);\n\t\t\tCheckListModel.getReference().saveCheckList(copy);\n\t\t\tloadCheckListsFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.MultiView.Module#getComponent()\n\t */\n\t@Override\n\tpublic JComponent getComponent() {\n\t\tif (!isInitialized) {\n\t\t\t// initialize UI\n\t\t\tinitializeComponents();\n\n\t\t\tnew PopupMenuHelper(checkListListTable,\n\t\t\t\t\tnew PopupMenuHelper.Entry[] { new PopupMenuHelper.Entry(\n\t\t\t\t\t\t\trenameAction, \"Rename\") });\n\n\t\t\trefresh();\n\n\t\t\t// listen for checkList model changes\n\t\t\tCheckListModel.getReference().addListener(this);\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.DockableView#getFrameTitle()\n\t */\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.MultiView.Module#getModuleName()\n\t */\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"CheckLists\");\n\t}\n\n\t/**\n\t * Gets the selected checkList name.\n\t * \n\t * @return the selected checkList name\n\t */\n\tprivate String getSelectedCheckListName() {\n\t\tint row = checkListListTable.getSelectedRow();\n\t\tif (row == -1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tTableSorter tm = (TableSorter) checkListListTable.getModel();\n\t\tString checkListName = (String) tm.getValueAt(row, 0);\n\t\treturn checkListName;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.MultiView.Module#getViewType()\n\t */\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.CHECKLIST;\n\t}\n\n\t/**\n\t * This method initializes the UI.\n\t * \n\t * */\n\tprivate void initializeComponents() {\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\t// *****************************\n\t\t// checkList split pane\n\t\t// *****************************\n\t\tJSplitPane checkListSplitPane = new JSplitPane();\n\t\tcheckListSplitPane.setResizeWeight(0.2D);\n\n\t\tJScrollPane checkListListScroll = new JScrollPane();\n\t\tcheckListListScroll\n\t\t\t\t.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n\t\tcheckListListScroll.setPreferredSize(new Dimension(100, 423));\n\n\t\tcheckListListTable = new JTable();\n\t\tcheckListListTable\n\t\t\t\t.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tcheckListListTable.setShowGrid(true);\n\t\tcheckListListTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tcheckListListScroll.setViewportView(checkListListTable);\n\n\t\t// table will contain only checkList names\n\t\tcheckListListTable.setModel(new TableSorter(new String[] { Resource\n\t\t\t\t.getResourceString(\"CheckList_Name\") },\n\t\t\t\tnew Class[] { java.lang.String.class }));\n\t\tListSelectionModel rowSM = checkListListTable.getSelectionModel();\n\t\trowSM.addListSelectionListener(this);\n\n\t\tcheckListSplitPane.setLeftComponent(checkListListScroll);\n\t\tJScrollPane checkListTextScroll = new JScrollPane();\n\t\tcheckListTextScroll.setPreferredSize(new Dimension(400, 400));\n\n\t\titemTable = new JTable();\n\n\t\tdefaultTextRenderer = itemTable.getDefaultRenderer(String.class);\n\t\titemTable.setDefaultRenderer(String.class, new ItemTextRenderer());\n\n\t\tTableSorter ts = new TableSorter(new String[] { \"\",\n\t\t\t\tResource.getResourceString(\"Item\") }, new Class[] {\n\t\t\t\tBoolean.class, String.class }, new boolean[] { true, true });\n\t\titemTable.setModel(ts);\n\n\t\titemTable.getColumnModel().getColumn(COMPLETE_COLUMN).setMaxWidth(30);\n\t\titemTable.getColumnModel().getColumn(COMPLETE_COLUMN).setMinWidth(20);\n\n\t\titemTable.getModel().addTableModelListener(this);\n\n\t\titemTable.getSelectionModel().setSelectionMode(\n\t\t\t\tListSelectionModel.SINGLE_SELECTION);\n\n\n\t\tts.addMouseListenerToHeaderInTable(itemTable);\n\n\t\t// popup menu\n\t\tnew PopupMenuHelper(itemTable, new PopupMenuHelper.Entry[] {\n\t\t\t\tnew PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\t\t\t\t\tint index = itemTable.getSelectedRow();\n\t\t\t\t\t\tObject[] row = new Object[2];\n\t\t\t\t\t\trow[COMPLETE_COLUMN] = Boolean.FALSE;\n\t\t\t\t\t\trow[TEXT_COLUMN] = \"\";\n\t\t\t\t\t\tmodel.insertRow(index, row);\n\t\t\t\t\t}\n\t\t\t\t}, \"Insert_Above\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\t\t\t\t\tint index = itemTable.getSelectedRow();\n\t\t\t\t\t\tObject[] row = new Object[2];\n\t\t\t\t\t\trow[COMPLETE_COLUMN] = Boolean.FALSE;\n\t\t\t\t\t\trow[TEXT_COLUMN] = \"\";\n\t\t\t\t\t\tmodel.insertRow(index + 1, row);\n\t\t\t\t\t}\n\t\t\t\t}, \"Insert_Below\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tremoveRow();\n\t\t\t\t\t}\n\t\t\t\t}, \"Remove\") });\n\n\t\tcheckListTextScroll.setViewportView(itemTable);\n\t\titemTable.setShowGrid(true);\n\t\titemTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tcheckListSplitPane.setRightComponent(checkListTextScroll);\n\t\tthis.add(checkListSplitPane, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t// *****************************\n\t\t// panel for dates and private check box\n\t\t// *****************************\n\t\tGridBagConstraints gridBagConstraints1 = GridBagConstraintsFactory\n\t\t\t\t.create(0, 0, GridBagConstraints.HORIZONTAL);\n\n\t\tgridBagConstraints1.anchor = GridBagConstraints.WEST;\n\n\t\t// *****************************\n\t\t// button panel\n\t\t// *****************************\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.setLayout(new FlowLayout());\n\n\t\tJButton newButton = new JButton();\n\t\tnewButton.setText(Resource.getResourceString(\"New_CheckList\"));\n\t\tnewButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tnewCheckList();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(newButton, null);\n\n\t\tJButton saveButton = new JButton();\n\t\tsaveButton.setText(Resource.getResourceString(\"Save_CheckList\"));\n\t\tsaveButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tsaveCheckList();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(saveButton, null);\n\n\t\tJButton deleteButton = new JButton();\n\t\tdeleteButton.setText(Resource.getResourceString(\"Delete_CheckList\"));\n\t\tdeleteButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tdeleteCheckList();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(deleteButton, null);\n\n\t\tJButton copyButton = new JButton();\n\t\tcopyButton.setText(Resource.getResourceString(\"Copy_CheckList\"));\n\t\tcopyButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcopyCheckList();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(copyButton, null);\n\n\t\tJButton uncheckButton = new JButton();\n\t\tuncheckButton.setText(Resource.getResourceString(\"Uncheck_All\"));\n\t\tuncheckButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tuncheckAll();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(uncheckButton, null);\n\n\t\tthis.add(buttonPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n\t\tJPanel sidePanel = new JPanel();\n\t\tsidePanel.setLayout(new GridBagLayout());\n\t\tJButton upButton = new JButton();\n\t\tupButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Up16.gif\"));\n\t\tsidePanel.add(upButton, GridBagConstraintsFactory.create(0, 0));\n\t\tupButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\t\t\tint index = itemTable.getSelectedRow();\n\t\t\t\tif (index > 0) {\n\t\t\t\t\tmodel.moveRow(index, index - 1);\n\t\t\t\t\titemTable.getSelectionModel().setSelectionInterval(\n\t\t\t\t\t\t\tindex - 1, index - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tJButton downButton = new JButton();\n\t\tdownButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Down16.gif\"));\n\t\tdownButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\t\t\tint index = itemTable.getSelectedRow();\n\t\t\t\tif (index < model.getRowCount() - 1) {\n\t\t\t\t\tmodel.moveRow(index, index + 1);\n\t\t\t\t\titemTable.getSelectionModel().setSelectionInterval(\n\t\t\t\t\t\t\tindex + 1, index + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tsidePanel.add(downButton, GridBagConstraintsFactory.create(0, 1));\n\n\t\tthis.add(sidePanel,\n\t\t\t\tGridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH));\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.MultiView.Module#initialize(net.sf.borg.ui.MultiView)\n\t */\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\n\t\t\t\t\t\t\"/resource/Preferences16.gif\"), getModuleName(),\n\t\t\t\tnew ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(ViewType.CHECKLIST);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.CHECKLIST);\n\t\t\t}\n\t\t});\n\n\t}\n\n\t/**\n\t * Load all checkLists from the model.\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tprivate void loadCheckListsFromModel() throws Exception {\n\t\tcheckListListTable.clearSelection();\n\t\tTableSorter tm = (TableSorter) checkListListTable.getModel();\n\t\ttm.setRowCount(0);\n\t\tCollection<String> names = CheckListModel.getReference().getNames();\n\t\tIterator<String> it = names.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\ttm.addRow(new Object[] { it.next() });\n\t\t}\n\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\tmodel.setRowCount(0);\n\t\tisCheckListEdited = false;\n\n\t}\n\n\t/**\n\t * Load items from a checklist into the item table.\n\t * \n\t * @param cl\n\t *            the checklist\n\t */\n\tprivate void loadItems(CheckList cl) {\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\tmodel.setRowCount(0);\n\n\t\tif (cl != null) {\n\t\t\tfor (CheckList.Item item : cl.getItems()) {\n\t\t\t\tObject[] row = new Object[2];\n\t\t\t\trow[COMPLETE_COLUMN] = item.getChecked();\n\t\t\t\trow[TEXT_COLUMN] = item.getText();\n\t\t\t\tmodel.addRow(row);\n\t\t\t}\n\n\t\t\tObject[] addrow = new Object[2];\n\t\t\taddrow[COMPLETE_COLUMN] = Boolean.FALSE;\n\t\t\taddrow[TEXT_COLUMN] = \"\";\n\t\t\tmodel.addRow(addrow);\n\t\t}\n\n\t}\n\n\t/**\n\t * create a new checkList.\n\t */\n\tprivate void newCheckList() {\n\n\t\t// if the user is currently editing another checkList, confirm that we\n\t\t// should discard changes\n\t\tif (this.isCheckListEdited) {\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_CheckList\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\treturn;\n\t\t}\n\n\t\t// get checkList name\n\t\tString name = InputDialog.show(Resource\n\t\t\t\t.getResourceString(\"Enter_CheckList_Name\"), 50);\n\t\t\n\t\tif (name == null || name.trim().isEmpty())\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tCheckList existing = CheckListModel.getReference().getCheckList(\n\t\t\t\t\tname);\n\t\t\tif (existing != null) {\n\t\t\t\t// checkList name already used\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Existing_CheckList\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t}\n\n\t\t// create a new empty checkList and save\n\t\tCheckList m = new CheckList();\n\t\tm.setCheckListName(name);\n\t\ttry {\n\t\t\tCheckListModel.getReference().saveCheckList(m);\n\t\t\tisCheckListEdited = false;\n\t\t\tloadCheckListsFromModel();\n\t\t\tselectCheckList(name);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.MultiView.Module#print()\n\t */\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tString selectedCheckList = getSelectedCheckListName();\n\t\t\tif (selectedCheckList == null)\n\t\t\t\treturn;\n\t\t\tTablePrinter.printTable(itemTable);\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * refresh the UI. This does not do anything if the user is currently in the\n\t * middle of editing a checkList.\n\t */\n\t@Override\n\tpublic void refresh() {\n\n\t\t// if the user is editing a row, don't process the refresh\n\t\tif (isCheckListEdited)\n\t\t\treturn;\n\t\ttry {\n\t\t\tloadCheckListsFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Removes the selected row.\n\t */\n\tprivate void removeRow() {\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\tint index = itemTable.getSelectedRow();\n\t\tint mapped = model.getMappedIndex(index);\n\t\tString s = (String) model.getValueAt(mapped, TEXT_COLUMN);\n\t\tif ((s == null || s.isEmpty()) && index == model.getRowCount()-1)\n\t\t\treturn;\n\t\tmodel.removeRow(mapped);\n\t}\n\n\t/**\n\t * Save the selected checkList to the model.\n\t */\n\tprivate void saveCheckList() {\n\t\tString name = getSelectedCheckListName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_CheckList_Warning\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tCheckList m = CheckListModel.getReference().getCheckList(name);\n\t\t\tsetItems(m);\n\n\t\t\tCheckListModel.getReference().saveCheckList(m);\n\t\t\tisCheckListEdited = false;\n\t\t\tloadCheckListsFromModel();\n\t\t\tselectCheckList(name);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * select the named checkList for editing.\n\t * \n\t * @param checkListName\n\t *            the checkList name\n\t */\n\tpublic void selectCheckList(String checkListName) {\n\t\tTableSorter tm = (TableSorter) checkListListTable.getModel();\n\t\tint rows = tm.getRowCount();\n\t\tfor (int i = 0; i < rows; ++i) {\n\t\t\tString name = (String) tm.getValueAt(i, 0);\n\t\t\tif (checkListName.equals(name)) {\n\t\t\t\t// select the checkList in the table. this will fire events that\n\t\t\t\t// cause us to open the checkList for edit\n\t\t\t\tcheckListListTable.getSelectionModel().setSelectionInterval(i,\n\t\t\t\t\t\ti);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sets the items in the checklist object from the item table contents.\n\t * \n\t * @param cl\n\t *            the new items\n\t */\n\tprivate void setItems(CheckList cl) {\n\t\tif (itemTable.isEditing())\n\t\t\titemTable.getCellEditor().stopCellEditing();\n\n\t\tcl.getItems().clear();\n\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\n\t\tfor (int i = 0; i < model.getRowCount(); i++) {\n\t\t\tCheckList.Item item = new CheckList.Item();\n\t\t\titem.setChecked((Boolean) model.getValueAt(i, COMPLETE_COLUMN));\n\t\t\titem.setText((String) model.getValueAt(i, TEXT_COLUMN));\n\t\t\tif (!item.getText().isEmpty())\n\t\t\t\tcl.getItems().add(item);\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @seejavax.swing.event.TableModelListener#tableChanged(javax.swing.event.\n\t * TableModelEvent)\n\t */\n\t@Override\n\tpublic void tableChanged(TableModelEvent arg0) {\n\n\t\t// ignore the table sorters events - we only care about the\n\t\t// underlying table model - which is tablesorter.newtablemodel\n\t\t// tablesorter is crap\n\t\tif (arg0.getSource() instanceof TableSorter)\n\t\t\treturn;\n\n\t\t// ignore insert - this only happens when blank rows are added\n\t\tif (arg0.getType() == TableModelEvent.INSERT)\n\t\t\treturn;\n\n\t\tisCheckListEdited = true;\n\t\teditedCheckListIndex = checkListListTable.getSelectedRow();\n\n\t\t// always maintain a new \"add\" row as the table is updated (without\n\t\t// being saved)\n\n\t\t// ignore delete events when deciding to add a blank row\n\t\t// problem caused when the table is cleared\n\t\tif (arg0.getType() == TableModelEvent.DELETE)\n\t\t\treturn;\n\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\tfor (int i = 0; i < model.getRowCount(); i++) {\n\t\t\tString text = (String) model.getValueAt(i, TEXT_COLUMN);\n\t\t\tif (text.isEmpty()) {\n\t\t\t\t// already has a blank row - so don't add one\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tObject[] addrow = new Object[2];\n\t\taddrow[COMPLETE_COLUMN] = Boolean.FALSE;\n\t\taddrow[TEXT_COLUMN] = \"\";\n\t\tmodel.addRow(addrow);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * net.sf.borg.model.Model.Listener#update(net.sf.borg.model.Model.ChangeEvent\n\t * )\n\t */\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * react to the user selecting a checkList in the checkList list. open it\n\t * for edit\n\t * \n\t */\n\t@Override\n\tpublic void valueChanged(ListSelectionEvent e) {\n\t\t// Ignore extra messages.\n\t\tif (e.getValueIsAdjusting())\n\t\t\treturn;\n\n\t\t// if the user has edited a checkList and is changing the checkList\n\t\t// selection...\n\t\tif (isCheckListEdited) {\n\n\t\t\t// if the selection is remaining (or returning) to the\n\t\t\t// edited checkList\n\t\t\t// then do nothing - may be returning to the edited checkList\n\t\t\t// due to the\n\t\t\t// setSelectionInterval line\n\t\t\t// below that resets the selection\n\t\t\tif (editedCheckListIndex == checkListListTable.getSelectedRow())\n\t\t\t\treturn;\n\n\t\t\t// selection is moving to a new checkList - prompt about\n\t\t\t// discarding\n\t\t\t// changes\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_CheckList\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\n\t\t\t// if user does not want to lose changes, we need to set\n\t\t\t// the\n\t\t\t// selection back to the edited checkList\n\t\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\t\tcheckListListTable.getSelectionModel().setSelectionInterval(\n\t\t\t\t\t\teditedCheckListIndex, editedCheckListIndex);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tString checkListName = getSelectedCheckListName();\n\t\tif (checkListName == null) {\n\t\t\tthis.loadItems(null);\n\t\t} else {\n\n\t\t\t// show the selected checkList\n\t\t\ttry {\n\t\t\t\tCheckList m = CheckListModel.getReference().getCheckList(\n\t\t\t\t\t\tcheckListName);\n\t\t\t\tthis.loadItems(m);\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\t\tisCheckListEdited = false;\n\n\t}\n\n\tprivate void uncheckAll() {\n\t\t\n\t\tString name = getSelectedCheckListName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_CheckList_Warning\"));\n\t\t\treturn;\n\t\t}\n\n\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\tResource.getResourceString(\"Uncheck_All\") + \"?\",\n\t\t\t\tResource.getResourceString(\"Uncheck_All\"),\n\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\n\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\treturn;\n\t\t}\n\n\t\tTableSorter model = (TableSorter) itemTable.getModel();\n\t\tfor (int i = 0; i < model.getRowCount(); i++) {\n\t\t\tmodel.setValueAt(Boolean.FALSE, i, COMPLETE_COLUMN);\n\t\t}\n\t\tmodel.fireTableDataChanged();\n\t}\n\n\t@Override\n\tpublic void cleanUp() {\n\t\t// on close - unselect checklist\n\t\tcheckListListTable.clearSelection();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/checklist/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  UI for Checklists.\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/link/LinkPanel.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.link;\n\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Desktop;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JFrame;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.table.TableColumn;\nimport javax.swing.table.TableColumnModel;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.LinkModel;\nimport net.sf.borg.model.LinkModel.LinkType;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Address;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.model.entity.KeyedEntity;\nimport net.sf.borg.model.entity.Link;\nimport net.sf.borg.model.entity.Memo;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.EntitySelector;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.address.AddressView;\nimport net.sf.borg.ui.calendar.AppointmentListView;\nimport net.sf.borg.ui.memo.MemoPanel;\nimport net.sf.borg.ui.task.ProjectView;\nimport net.sf.borg.ui.task.TaskView;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * Panel for managing links. This panel is included in the UIs for entities that\n * have links.\n */\npublic class LinkPanel extends JPanel implements Model.Listener {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Provide a human readable name for a link\n\t * \n\t * @param at\n\t *            the link\n\t * @return the name\n\t * @throws Exception\n\t */\n\tpublic static String getName(Link at) throws Exception {\n\t\tif (at.getLinkType().equals(LinkType.URL.toString()))\n\t\t\treturn at.getPath();\n\t\telse if (at.getLinkType().equals(LinkType.APPOINTMENT.toString())) {\n\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\tif (ap != null) {\n\t\t\t\treturn (Resource.getResourceString(\"appointment\") + \"[\"\n\t\t\t\t\t\t+ ap.getClearText() + \"]\");\n\t\t\t}\n\t\t} else if (at.getLinkType().equals(LinkType.PROJECT.toString())) {\n\t\t\tProject ap = TaskModel.getReference().getProject(\n\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\tif (ap != null) {\n\t\t\t\treturn (Resource.getResourceString(\"project\") + \"[\"\n\t\t\t\t\t\t+ ap.getDescription() + \"]\");\n\t\t\t}\n\t\t} else if (at.getLinkType().equals(LinkType.TASK.toString())) {\n\t\t\tTask ap = TaskModel.getReference().getTask(\n\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\tif (ap != null) {\n\t\t\t\treturn (Resource.getResourceString(\"task\") + \"[\"\n\t\t\t\t\t\t+ ap.getSummary() + \"]\");\n\t\t\t}\n\t\t} else if (at.getLinkType().equals(LinkType.ADDRESS.toString())) {\n\t\t\tAddress ap = AddressModel.getReference().getAddress(\n\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\tif (ap != null) {\n\t\t\t\treturn (Resource.getResourceString(\"Address\") + \"[\"\n\t\t\t\t\t\t+ ap.getLastName() + \",\" + ap.getFirstName() + \"]\");\n\t\t\t}\n\t\t} else if (at.getLinkType().equals(LinkType.MEMO.toString())) {\n\t\t\treturn (Resource.getResourceString(\"memo\") + \"[\" + at.getPath() + \"]\");\n\t\t} else if (at.getLinkType().equals(LinkType.FILELINK.toString())) {\n\t\t\tFile f = new File(at.getPath());\n\t\t\treturn f.getName();\n\t\t} else if (at.getLinkType().equals(LinkType.ATTACHMENT.toString())) {\n\t\t\treturn at.getPath();\n\t\t}\n\n\t\treturn \"error\";\n\t}\n\n\t/** the entity that owns the links */\n\tprivate KeyedEntity<?> owningEntity;\n\n\t/** table to show the links */\n\tprivate final JTable linkTable = new JTable();\n\n\t/* scroll panel to hold the links table */\n\tprivate final JScrollPane scrollPanel = new JScrollPane();\n\n\t/**\n\t * constructor\n\t */\n\tpublic LinkPanel() {\n\t\tinitComponents();\n\n\t\t// listen for link model changes\n\t\tLinkModel.getReference().addListener(this);\n\t}\n\t\n\tpublic void cleanup()\n\t{\n\t\tLinkModel.getReference().removeListener(this);\n\t}\n\n\t/**\n\t * indicate if the entity shown by this panel has any links\n\t * \n\t * @return true if the owning entity has links\n\t */\n\tpublic boolean hasLinks() {\n\n\t\ttry {\n\t\t\tCollection<Link> atts = LinkModel.getReference().getLinks(\n\t\t\t\t\towningEntity);\n\t\t\tif (!atts.isEmpty())\n\t\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * initialize the UI\n\t */\n\tpublic void initComponents() {\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\tlinkTable\n\t\t\t\t.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\n\n\t\t// table model will contain link name and key.\n\t\tlinkTable.setModel(new TableSorter(new String[] { \"\", \"Key\" },\n\t\t\t\tnew Class[] { java.lang.String.class, Integer.class }));\n\n\t\t// set up for sorting when a column header is clicked\n\t\tTableSorter tm = (TableSorter) linkTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(linkTable);\n\n\t\t// set column widths\n\t\tlinkTable.getColumnModel().getColumn(0).setPreferredWidth(125);\n\t\tlinkTable.getColumnModel().getColumn(1).setPreferredWidth(75);\n\n\t\tlinkTable.setPreferredScrollableViewportSize(new Dimension(150, 100));\n\n\t\t// on double-click - open the object referenced by the link\n\t\tlinkTable.addMouseListener(new java.awt.event.MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(java.awt.event.MouseEvent evt) {\n\t\t\t\tif (evt.getClickCount() < 2)\n\t\t\t\t\treturn;\n\t\t\t\topenSelected();\n\t\t\t}\n\t\t});\n\n\t\t// add a popup menu to the link table\n\t\tnew PopupMenuHelper(linkTable, new PopupMenuHelper.Entry[] {\n\t\t\t\t// open menu item\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\topenSelected();\n\t\t\t\t\t}\n\t\t\t\t}, \"Open\"),\n\t\t\t\t// delete menu item\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tTableSorter ts = (TableSorter) linkTable.getModel();\n\t\t\t\t\t\tint[] indices = linkTable.getSelectedRows();\n\t\t\t\t\t\tList<Integer> keys = new ArrayList<Integer>();\n\n\t\t\t\t\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\t\t\t\t\tint index = indices[i];\n\t\t\t\t\t\t\tInteger key = (Integer) ts.getValueAt(index, 1);\n\t\t\t\t\t\t\tkeys.add(key);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (Integer key : keys) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tLinkModel.getReference().delete(key.intValue());\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}, \"Delete\"),\n\t\t\t\t// show link properties menu item\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tTableSorter ts = (TableSorter) linkTable.getModel();\n\t\t\t\t\t\tint[] indices = linkTable.getSelectedRows();\n\t\t\t\t\t\t// show link type and path\n\t\t\t\t\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\t\t\t\t\tint index = indices[i];\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tInteger key = (Integer) ts.getValueAt(index, 1);\n\t\t\t\t\t\t\t\tLink l = LinkModel.getReference().getLink(\n\t\t\t\t\t\t\t\t\t\tkey.intValue());\n\t\t\t\t\t\t\t\tString info = Resource\n\t\t\t\t\t\t\t\t\t\t.getResourceString(\"Type\")\n\t\t\t\t\t\t\t\t\t\t+ \":\"\n\t\t\t\t\t\t\t\t\t\t+ l.getLinkType()\n\t\t\t\t\t\t\t\t\t\t+ \"\\n\\n[\"\n\t\t\t\t\t\t\t\t\t\t+ l.getPath() + \"]\";\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().notice(info);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}, \"Properties\") });\n\n\t\t// hide the key column from view\n\t\tTableColumnModel tcm = linkTable.getColumnModel();\n\t\tTableColumn column = tcm.getColumn(1);\n\t\ttcm.removeColumn(column);\n\n\t\t// add a scroll to the table\n\t\tscrollPanel.setViewportView(linkTable);\n\t\tthis.add(scrollPanel, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t// add the link file button\n\t\tJButton linkFilebutton = new JButton(\n\t\t\t\tResource.getResourceString(\"link_file\"));\n\t\tlinkFilebutton.setIcon(IconHelper.getIcon(\"/resource/Add16.gif\"));\n\t\tlinkFilebutton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\tif (owningEntity == null || owningEntity.getKey() == -1) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"att_owner_null\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// prompt for a file and create a link with the file's absolute\n\t\t\t\t// path\n\t\t\t\tFile file;\n\t\t\t\twhile (true) {\n\t\t\t\t\t// prompt for a file\n\t\t\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\t\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\t\t\t\tchooser.setDialogTitle(Resource\n\t\t\t\t\t\t\t.getResourceString(\"choose_file\"));\n\t\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\t\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\t\t\t\t\tfile = new File(s);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\tfile.getAbsolutePath(), LinkType.FILELINK);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// attach file button\n\t\tJButton attachFileButton = new JButton(\n\t\t\t\tResource.getResourceString(\"attach_file\"));\n\t\tattachFileButton.setIcon(IconHelper.getIcon(\"/resource/Copy16.gif\"));\n\t\tattachFileButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tif (owningEntity == null || owningEntity.getKey() == -1) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"att_owner_null\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// prompt or a file and add an attachment link\n\t\t\t\t// the model will copy the file to the attachement folder\n\t\t\t\tFile file;\n\t\t\t\twhile (true) {\n\t\t\t\t\t// prompt for a file\n\t\t\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\t\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\t\t\t\tchooser.setDialogTitle(Resource\n\t\t\t\t\t\t\t.getResourceString(\"choose_file\"));\n\t\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\t\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\t\t\t\t\tfile = new File(s);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\n\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\tfile.getAbsolutePath(), LinkType.ATTACHMENT);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// URL link button\n\t\tJButton urlLinkButton = new JButton(Resource.getResourceString(\"url\"));\n\t\turlLinkButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/WebComponent16.gif\"));\n\t\turlLinkButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\tif (owningEntity == null || owningEntity.getKey() == -1) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"att_owner_null\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// prompt for a url and save as a link\n\t\t\t\t// no validation is done\n\t\t\t\tString url = JOptionPane.showInputDialog(Resource\n\t\t\t\t\t\t.getResourceString(\"url\") + \"?\");\n\n\t\t\t\tif (url == null)\n\t\t\t\t\treturn; // cancel case\n\t\t\t\telse if (url.trim().isEmpty()) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"URL_is_Empty\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\n\t\t\t\ttry {\n\t\t\t\t\tLinkModel.getReference().addLink(owningEntity, url,\n\t\t\t\t\t\t\tLinkType.URL);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// borg entity link button\n\t\tJButton entityLinkbutton = new JButton(\"BORG\");\n\t\tentityLinkbutton.setIcon(IconHelper.getIcon(\"/resource/borg16.jpg\"));\n\t\tentityLinkbutton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\n\t\t\t\tif (owningEntity == null || owningEntity.getKey() == -1) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"att_owner_null\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// prompt for the entity type\n\t\t\t\tObject[] possibleValues = {\n\t\t\t\t\t\tResource.getResourceString(\"appointment\"),\n\t\t\t\t\t\tResource.getResourceString(\"task\"),\n\t\t\t\t\t\tResource.getResourceString(\"memo\"),\n\t\t\t\t\t\tResource.getResourceString(\"Address\"),\n\t\t\t\t\t\tResource.getResourceString(\"project\") };\n\t\t\t\tString selectedValue = (String) JOptionPane.showInputDialog(\n\t\t\t\t\t\tnull, Resource.getResourceString(\"select_link_type\"),\n\t\t\t\t\t\t\"BORG\", JOptionPane.INFORMATION_MESSAGE, null,\n\t\t\t\t\t\tpossibleValues, possibleValues[0]);\n\t\t\t\tif (selectedValue == null)\n\t\t\t\t\treturn;\n\t\t\t\ttry {\n\n\t\t\t\t\t// for each entity type, bring up a dialog to select the\n\t\t\t\t\t// entity instance and\n\t\t\t\t\t// then add the link\n\t\t\t\t\tif (selectedValue.equals(Resource\n\t\t\t\t\t\t\t.getResourceString(\"appointment\"))) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tList<Appointment> exclude = new ArrayList<Appointment>();\n\t\t\t\t\t\tif( owningEntity instanceof Appointment)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texclude.add((Appointment)owningEntity);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tAppointment ap = EntitySelector.selectAppointment(exclude);\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\t\t\tInteger.toString(ap.getKey()),\n\t\t\t\t\t\t\t\t\tLinkType.APPOINTMENT);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (selectedValue.equals(Resource\n\t\t\t\t\t\t\t.getResourceString(\"project\"))) {\n\t\t\t\t\t\tProject ap = EntitySelector.selectProject();\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\t\t\tInteger.toString(ap.getKey()),\n\t\t\t\t\t\t\t\t\tLinkType.PROJECT);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (selectedValue.equals(Resource\n\t\t\t\t\t\t\t.getResourceString(\"task\"))) {\n\t\t\t\t\t\tTask ap = EntitySelector.selectTask();\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\t\t\tInteger.toString(ap.getKey()),\n\t\t\t\t\t\t\t\t\tLinkType.TASK);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (selectedValue.equals(Resource\n\t\t\t\t\t\t\t.getResourceString(\"Address\"))) {\n\t\t\t\t\t\tAddress ap = EntitySelector.selectAddress();\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\t\t\tInteger.toString(ap.getKey()),\n\t\t\t\t\t\t\t\t\tLinkType.ADDRESS);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (selectedValue.equals(Resource\n\t\t\t\t\t\t\t.getResourceString(\"memo\"))) {\n\t\t\t\t\t\tMemo ap = EntitySelector.selectMemo();\n\t\t\t\t\t\tif (ap != null) {\n\t\t\t\t\t\t\tLinkModel.getReference().addLink(owningEntity,\n\t\t\t\t\t\t\t\t\tap.getMemoName(), LinkType.MEMO);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// button panel\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.add(linkFilebutton);\n\t\tbuttonPanel.add(attachFileButton);\n\t\tbuttonPanel.add(urlLinkButton);\n\t\tbuttonPanel.add(entityLinkbutton);\n\t\tthis.add(buttonPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.NONE, 1.0, 1.0));\n\n\t\t// if we can't figure out the attachement folder, then disable the\n\t\t// attachment button\n\t\t// attachments only work for hsql right now since we have a folder\n\t\t// readily available\n\t\tif (LinkModel.attachmentFolder() == null) {\n\t\t\tattachFileButton.setEnabled(false);\n\t\t}\n\n\t}\n\n\t/**\n\t * get the item referenced by the link and open it. The non-entity link\n\t * types depend on java.awt.Desktop to open them in the correct \"native\"\n\t * way. If the version of Java does not support desktop, then this method is\n\t * limited to only opening borg entities\n\t */\n\tpublic void openSelected() {\n\n\t\tTableSorter ts = (TableSorter) linkTable.getModel();\n\t\tint[] indices = linkTable.getSelectedRows();\n\n\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\tint index = indices[i];\n\t\t\ttry {\n\t\t\t\tInteger key = (Integer) ts.getValueAt(index, 1);\n\t\t\t\tLink at = LinkModel.getReference().getLink(key.intValue());\n\n\t\t\t\t// open a linked file\n\t\t\t\tif (at.getLinkType().equals(LinkType.FILELINK.toString())) {\n\t\t\t\t\tFile file = new File(at.getPath());\n\t\t\t\t\tif (!file.exists()) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (Desktop.isDesktopSupported() == false)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tDesktop.getDesktop().open(file);\n\t\t\t\t}\n\t\t\t\t// open an attachment\n\t\t\t\telse if (at.getLinkType()\n\t\t\t\t\t\t.equals(LinkType.ATTACHMENT.toString())) {\n\t\t\t\t\tFile file = new File(LinkModel.attachmentFolder() + \"/\"\n\t\t\t\t\t\t\t+ at.getPath());\n\t\t\t\t\tif (!file.exists()) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (Desktop.isDesktopSupported() == false)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tDesktop.getDesktop().open(file);\n\t\t\t\t}\n\t\t\t\t// open a url\n\t\t\t\telse if (at.getLinkType().equals(LinkType.URL.toString())) {\n\t\t\t\t\tif (Desktop.isDesktopSupported() == false)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tDesktop.getDesktop().browse(new URI(at.getPath()));\n\t\t\t\t}\n\t\t\t\t// open an appointment\n\t\t\t\telse if (at.getLinkType().equals(\n\t\t\t\t\t\tLinkType.APPOINTMENT.toString())) {\n\t\t\t\t\tAppointment ap = AppointmentModel.getReference().getAppt(\n\t\t\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\t\t\tif (ap == null) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\t\t\tcal.setTime(ap.getDate());\n\n\t\t\t\t\t// bring up an appt editor window\n\t\t\t\t\tAppointmentListView ag = new AppointmentListView(\n\t\t\t\t\t\t\tcal.get(Calendar.YEAR), cal.get(Calendar.MONTH),\n\t\t\t\t\t\t\tcal.get(Calendar.DATE));\n\t\t\t\t\tag.showView();\n\t\t\t\t\tag.showApp(ap.getKey());\n\n\t\t\t\t}\n\t\t\t\t// open a project\n\t\t\t\telse if (at.getLinkType().equals(LinkType.PROJECT.toString())) {\n\t\t\t\t\tProject ap = TaskModel.getReference().getProject(\n\t\t\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\t\t\tif (ap == null) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tnew ProjectView(ap, ProjectView.Action.CHANGE, null)\n\t\t\t\t\t\t\t.showView();\n\t\t\t\t}\n\t\t\t\t// open a task\n\t\t\t\telse if (at.getLinkType().equals(LinkType.TASK.toString())) {\n\t\t\t\t\tTask ap = TaskModel.getReference().getTask(\n\t\t\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\t\t\tif (ap == null) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tnew TaskView(ap, TaskView.Action.CHANGE, null).showView();\n\t\t\t\t}\n\t\t\t\t// open an address\n\t\t\t\telse if (at.getLinkType().equals(LinkType.ADDRESS.toString())) {\n\t\t\t\t\tAddress ap = AddressModel.getReference().getAddress(\n\t\t\t\t\t\t\tInteger.parseInt(at.getPath()));\n\t\t\t\t\tif (ap == null) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"att_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tnew AddressView(ap).showView();\n\t\t\t\t}\n\t\t\t\t// open a memo\n\t\t\t\telse if (at.getLinkType().equals(LinkType.MEMO.toString())) {\n\t\t\t\t\tComponent c = MultiView.getMainView()\n\t\t\t\t\t\t\t.setView(ViewType.MEMO);\n\n\t\t\t\t\t// show the actual memo\n\t\t\t\t\tif (c != null && c instanceof MemoPanel) {\n\t\t\t\t\t\tMemoPanel mp = (MemoPanel) c;\n\t\t\t\t\t\tmp.selectMemo(at.getPath());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * reload the data from the model and redisplay\n\t */\n\tpublic void refresh() {\n\n\t\tTableSorter tm = (TableSorter) linkTable.getModel();\n\t\ttm.setRowCount(0);\n\t\ttry {\n\t\t\tCollection<Link> atts = LinkModel.getReference().getLinks(\n\t\t\t\t\towningEntity);\n\t\t\tIterator<Link> it = atts.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tLink at = (it.next());\n\t\t\t\ttm.addRow(new Object[] { LinkPanel.getName(at),\n\t\t\t\t\t\tInteger.valueOf(at.getKey()) });\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\tscrollPanel.setVisible(tm.getRowCount() > 0);\n\n\t\tfor (Container p = this.getParent(); p != null; p = p.getParent()) {\n\t\t\tif (p instanceof JFrame) {\n\t\t\t\tp.validate();\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * set the owning entity of this panel\n\t * \n\t * @param owner\n\t *            owning entity\n\t */\n\tpublic void setOwner(KeyedEntity<?> owner) {\n\n\t\towningEntity = owner;\n\t\trefresh();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/link/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  UI for Links\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/memo/MemoPanel.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.memo;\n\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.KeyListener;\nimport java.awt.print.PrinterException;\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Collection;\nimport java.util.Iterator;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComponent;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.event.ListSelectionListener;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.LinkModel;\nimport net.sf.borg.model.LinkModel.LinkType;\nimport net.sf.borg.model.MemoModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.entity.Link;\nimport net.sf.borg.model.entity.Memo;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.HTMLLimitDocument;\nimport net.sf.borg.ui.util.HTMLTextPane;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.InputDialog;\nimport net.sf.borg.ui.util.PasswordHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * UI for editing memos. It has a table that shows all memos by name and an\n * editing panel for editing memo text.\n */\npublic class MemoPanel extends DockableView implements ListSelectionListener,\n\t\tModule {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The memo date format. */\n\tprivate final SimpleDateFormat memoDateFormat = new SimpleDateFormat(\n\t\t\t\"MM/dd/yyyy hh:mm aa\");\n\n\t/** The date label. */\n\tprivate final JLabel dateLabel = new JLabel();\n\n\t/** The edited memo index. */\n\tprivate int editedMemoIndex = -1;\n\n\t/** is memo changed flag */\n\tprivate boolean isMemoEdited = false;\n\n\t/** The memo list table. */\n\tprivate JTable memoListTable = null;\n\n\t/** The memo text. */\n\tprivate HTMLTextPane memoText = null;\n\n\t/** The save button. */\n\tprivate JButton saveButton = null;\n\n\t/** decrypt button */\n\tprivate JButton decryptButton = null;\n\n\tprivate boolean isInitialized = false;\n\n\t/**\n\t * encryption checkbox\n\t */\n\tprivate JCheckBox encryptBox = null;\n\n\tprivate final ActionListener renameAction = new ActionListener() {\n\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent e) {\n\n\t\t\tString name = getSelectedMemoName();\n\t\t\tif (name == null) {\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Select_Memo_Warning\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isMemoEdited) {\n\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\tResource.getResourceString(\"Edited_Memo\"),\n\t\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tisMemoEdited = false;\n\n\t\t\tString newname = InputDialog.show(\n\t\t\t\t\tResource.getResourceString(\"Enter_Memo_Name\"), 50);\n\t\t\tif (newname == null || newname.isEmpty() || newname.equals(name))\n\t\t\t\treturn;\n\n\t\t\tif (newname.length() > 50) {\n\t\t\t\tErrmsg.getErrorHandler()\n\t\t\t\t\t\t.notice(Resource.getResourceString(\n\t\t\t\t\t\t\t\t\"max_length\",\n\t\t\t\t\t\t\t\tnew String[] {\n\t\t\t\t\t\t\t\t\t\tResource.getResourceString(\"Memo_Name\"),\n\t\t\t\t\t\t\t\t\t\t\"50\" }));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMemo existing = MemoModel.getReference().getMemo(newname);\n\t\t\t\tif (existing != null) {\n\t\t\t\t\t// memo name already used\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"Existing_Memo\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\n\t\t\tMemo m;\n\t\t\ttry {\n\n\t\t\t\t\n\t\t\t\tm = MemoModel.getReference().getMemo(name);\n\t\t\t\tm.setMemoName(newname);\n\t\t\t\tMemoModel.getReference().saveMemo(m);\n\t\t\t\t\n\t\t\t\t// move any links to this memo\n\t\t\t\tCollection<Link> links = LinkModel.getReference().getLinks();\n\t\t\t\tfor (Link link : links) {\n\t\t\t\t\tif (link.getLinkType().equals(LinkType.MEMO.toString())\n\t\t\t\t\t\t\t&& link.getPath().equals(name)) {\n\t\t\t\t\t\tlink.setPath(newname);\n\t\t\t\t\t\tLinkModel.getReference().saveLink(link);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tMemoModel.getReference().delete(name, false);\n\n\t\t\t\tloadMemosFromModel();\n\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\n\t\t}\n\n\t};\n\n\t/**\n\t * constructor.\n\t */\n\tpublic MemoPanel() {\n\t\tsuper();\n\n\t}\n\n\t/**\n\t * Delete the selected memo form the model.\n\t */\n\tprivate void deleteMemo() {\n\t\tString name = getSelectedMemoName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_Memo_Warning\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// confirm delete\n\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\tResource.getResourceString(\"Really_Delete_\") + \"?\",\n\t\t\t\tResource.getResourceString(\"Confirm_Delete\"),\n\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tMemoModel.getReference().delete(name, false);\n\t\t\tisMemoEdited = false;\n\t\t\tloadMemosFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Gets the selected memo name.\n\t * \n\t * @return the selected memo name\n\t */\n\tprivate String getSelectedMemoName() {\n\t\tint row = memoListTable.getSelectedRow();\n\t\tif (row == -1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tTableSorter tm = (TableSorter) memoListTable.getModel();\n\t\tString memoName = (String) tm.getValueAt(row, 0);\n\t\treturn memoName;\n\t}\n\n\t/**\n\t * This method initializes the UI.\n\t * \n\t * */\n\tprivate void initializeComponents() {\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\t// *****************************\n\t\t// memo split pane\n\t\t// *****************************\n\t\tJSplitPane memoSplitPane = new JSplitPane();\n\t\tmemoSplitPane.setResizeWeight(0.2D);\n\n\t\tJScrollPane memoListScroll = new JScrollPane();\n\t\tmemoListScroll\n\t\t\t\t.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n\t\tmemoListScroll.setPreferredSize(new Dimension(100, 423));\n\n\t\tmemoListTable = new JTable();\n\t\tmemoListTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\tmemoListTable.setShowGrid(true);\n\t\tmemoListTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tmemoListScroll.setViewportView(memoListTable);\n\n\t\t// table will contain only memo names\n\t\tmemoListTable.setModel(new TableSorter(new String[] { Resource\n\t\t\t\t.getResourceString(\"Memo_Name\") },\n\t\t\t\tnew Class[] { java.lang.String.class }));\n\t\tListSelectionModel rowSM = memoListTable.getSelectionModel();\n\t\trowSM.addListSelectionListener(this);\n\n\t\tmemoSplitPane.setLeftComponent(memoListScroll);\n\t\tJScrollPane memoTextScroll = new JScrollPane();\n\t\tmemoTextScroll.setPreferredSize(new Dimension(400, 400));\n\n\t\tHTMLLimitDocument newLimitDocument = new HTMLLimitDocument();\n\t\tnewLimitDocument.setMaxLength(Prefs.getIntPref(PrefName.MAX_TEXT_SIZE));\n\n\t\tmemoText = new HTMLTextPane(newLimitDocument);\n\n\t\t// if the memo text is edited, then set a change flag and enable the\n\t\t// save button\n\t\tmemoText.getDocument().addDocumentListener(new DocumentListener() {\n\n\t\t\t@Override\n\t\t\tpublic void changedUpdate(DocumentEvent arg0) {\n\t\t\t\tisMemoEdited = true;\n\t\t\t\teditedMemoIndex = memoListTable.getSelectedRow();\n\t\t\t\tsaveButton.setEnabled(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void insertUpdate(DocumentEvent arg0) {\n\t\t\t\tisMemoEdited = true;\n\t\t\t\teditedMemoIndex = memoListTable.getSelectedRow();\n\t\t\t\tsaveButton.setEnabled(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void removeUpdate(DocumentEvent arg0) {\n\t\t\t\tisMemoEdited = true;\n\t\t\t\teditedMemoIndex = memoListTable.getSelectedRow();\n\t\t\t\tsaveButton.setEnabled(true);\n\t\t\t}\n\t\t});\n\n\t\tmemoText.addKeyListener(new KeyListener() {\n\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent arg0) {\n\t\t\t\tif (arg0.isControlDown() && arg0.getKeyCode() == KeyEvent.VK_F) {\n\t\t\t\t\tdoFind();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void keyReleased(KeyEvent arg0) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void keyTyped(KeyEvent arg0) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t});\n\t\tmemoTextScroll.setViewportView(memoText);\n\t\tmemoSplitPane.setRightComponent(memoTextScroll);\n\t\tthis.add(memoSplitPane, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t// *****************************\n\t\t// panel for dates and private check box\n\t\t// *****************************\n\t\tGridBagConstraints gridBagConstraints1 = GridBagConstraintsFactory\n\t\t\t\t.create(0, 0, GridBagConstraints.HORIZONTAL);\n\n\t\tgridBagConstraints1.anchor = GridBagConstraints.WEST;\n\n\t\tJPanel dateAndPrivatePanel = new JPanel();\n\t\tdateAndPrivatePanel.setLayout(new GridBagLayout());\n\t\tdateAndPrivatePanel.add(dateLabel, gridBagConstraints1);\n\n\t\tthis.add(dateAndPrivatePanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.HORIZONTAL));\n\n\t\t// *****************************\n\t\t// button panel\n\t\t// *****************************\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.setLayout(new FlowLayout());\n\n\t\tJButton newButton = new JButton();\n\t\tnewButton.setText(Resource.getResourceString(\"New_Memo\"));\n\t\tnewButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tnewMemo();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(newButton, null);\n\n\t\tsaveButton = new JButton();\n\t\tsaveButton.setText(Resource.getResourceString(\"Save_Memo\"));\n\t\tsaveButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tsaveMemo();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(saveButton, null);\n\n\t\tJButton deleteButton = new JButton();\n\t\tdeleteButton.setText(Resource.getResourceString(\"Delete_Memo\"));\n\t\tdeleteButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tdeleteMemo();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(deleteButton, null);\n\n\t\tJButton exportButton = new JButton();\n\t\texportButton.setText(Resource.getResourceString(\"export\"));\n\t\texportButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t// export a single memo to a file\n\n\t\t\t\tString name = getSelectedMemoName();\n\t\t\t\tif (name == null) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"Select_Memo_Warning\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (decryptButton.isEnabled()) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\t\tResource.getResourceString(\"Export_Memo_Decrypt\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tStringBuffer sb = new StringBuffer();\n\t\t\t\tString s = memoText.getPlainText();\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tif (s.charAt(i) == '\\n') {\n\t\t\t\t\t\tsb.append('\\r');\n\t\t\t\t\t}\n\t\t\t\t\tsb.append(s.charAt(i));\n\n\t\t\t\t}\n\t\t\t\tbyte[] buf2 = sb.toString().getBytes();\n\t\t\t\tByteArrayInputStream istr = new ByteArrayInputStream(buf2);\n\t\t\t\ttry {\n\t\t\t\t\tfileSave(\".\", istr, \"\");\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(exportButton, null);\n\n\t\tdecryptButton = new JButton();\n\t\tdecryptButton.setText(Resource.getResourceString(\"decrypt\"));\n\t\tdecryptButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tMemo m = MemoModel.getReference().getMemo(\n\t\t\t\t\t\t\tgetSelectedMemoName());\n\t\t\t\t\tString pw = PasswordHelper.getReference().getEncryptionKeyWithTimeout(\"Decrypt Memo\");\n\t\t\t\t\tif (pw == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tm.decrypt(pw);\n\n\t\t\t\t\tmemoText.setText(m.getMemoText());\n\t\t\t\t\tmemoText.setEditable(true);\n\t\t\t\t\tdecryptButton.setEnabled(false);\n\t\t\t\t\tsaveButton.setEnabled(false);\n\t\t\t\t\tclearEditFlag();\n\t\t\t\t\tmemoText.setCaretPosition(0);\n\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(decryptButton, null);\n\n\t\tencryptBox = new JCheckBox();\n\t\tencryptBox.setText(Resource.getResourceString(\"EncryptOnSave\"));\n\t\tbuttonPanel.add(encryptBox, null);\n\n\t\tthis.add(buttonPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\n\t}\n\n\tprivate static void fileSave(String startDirectory, InputStream istr,\n\t\t\tString defaultFilename) throws Exception {\n\t\tJFileChooser chooser = new JFileChooser();\n\n\t\tchooser.setCurrentDirectory(new File(startDirectory));\n\t\tchooser.setDialogTitle(Resource.getResourceString(\"Save\"));\n\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\tint returnVal = chooser.showSaveDialog(null);\n\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\treturn;\n\n\t\tString s = chooser.getSelectedFile().getAbsolutePath();\n\t\tFileOutputStream ostr = new FileOutputStream(s);\n\n\t\tint b;\n\t\twhile ((b = istr.read()) != -1)\n\t\t\tostr.write(b);\n\n\t\tistr.close();\n\t\tostr.close();\n\t}\n\n\t/**\n\t * Load memos from the model\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tprivate void loadMemosFromModel() throws Exception {\n\t\tmemoListTable.clearSelection();\n\t\tTableSorter tm = (TableSorter) memoListTable.getModel();\n\t\ttm.setRowCount(0);\n\t\tCollection<String> names = MemoModel.getReference().getNames();\n\t\tIterator<String> it = names.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\ttm.addRow(new Object[] { it.next() });\n\t\t}\n\t}\n\n\t/**\n\t * create a new memo\n\t */\n\tprivate void newMemo() {\n\n\t\t// if the user is currently editing another memo, confirm that we\n\t\t// should discard changes\n\t\tif (this.isMemoEdited) {\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_Memo\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\treturn;\n\t\t}\n\n\t\t// get memo name\n\t\tString name = InputDialog.show(\n\t\t\t\tResource.getResourceString(\"Enter_Memo_Name\"), 50);\n\n\t\tif (name == null || name.trim().isEmpty())\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tMemo existing = MemoModel.getReference().getMemo(name);\n\t\t\tif (existing != null) {\n\t\t\t\t// memo name already used\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Existing_Memo\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t}\n\n\t\t// create a new empty memo and save\n\t\tMemo m = new Memo();\n\t\tm.setMemoName(name);\n\t\ttry {\n\t\t\tMemoModel.getReference().saveMemo(m);\n\t\t\tisMemoEdited = false;\n\t\t\tloadMemosFromModel();\n\t\t\tselectMemo(name);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * refresh the UI. This does not do anything if the user is currently in the\n\t * middle of editing a memo.\n\t */\n\t@Override\n\tpublic void refresh() {\n\n\t\t// if the user is editing a row, don't process the refresh\n\t\tif (isMemoEdited)\n\t\t\treturn;\n\t\ttry {\n\t\t\tloadMemosFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * Save the selected memo to the model\n\t */\n\tprivate void saveMemo() {\n\t\tString name = getSelectedMemoName();\n\t\tif (name == null) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"Select_Memo_Warning\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tMemo m = MemoModel.getReference().getMemo(name);\n\n\t\t\tif (m == null) {\n\t\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\t\tResource.getResourceString(\"Memo_not_exist\"));\n\t\t\t\tisMemoEdited = false;\n\t\t\t\tloadMemosFromModel();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tm.setMemoText(memoText.getPlainText());\n\t\t\tm.setEncrypted(false);\n\t\t\tif (encryptBox.isSelected()) {\n\t\t\t\tString pw = PasswordHelper.getReference().getEncryptionKeyWithTimeout(\"Encrypt Memo\");\n\t\t\t\tif (pw == null)\n\t\t\t\t\treturn;\n\t\t\t\tm.encrypt(pw);\n\t\t\t}\n\t\t\tMemoModel.getReference().saveMemo(m);\n\t\t\tisMemoEdited = false;\n\t\t\tloadMemosFromModel();\n\t\t\tselectMemo(name);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * selecte the named memo for editing\n\t * \n\t * @param memoName\n\t *            the memo name\n\t */\n\tpublic void selectMemo(String memoName) {\n\t\tTableSorter tm = (TableSorter) memoListTable.getModel();\n\t\tint rows = tm.getRowCount();\n\t\tfor (int i = 0; i < rows; ++i) {\n\t\t\tString name = (String) tm.getValueAt(i, 0);\n\t\t\tif (memoName.equals(name)) {\n\t\t\t\t// select the memo in the table. this will fire events that\n\t\t\t\t// cause us to open the memo for edit\n\t\t\t\tmemoListTable.getSelectionModel().setSelectionInterval(i, i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * react to the user selecting a memo in the memo list. open it for edit\n\t */\n\t@Override\n\tpublic void valueChanged(ListSelectionEvent e) {\n\t\t// Ignore extra messages.\n\t\tif (e.getValueIsAdjusting())\n\t\t\treturn;\n\n\t\t// if the user has edited a memo and is changing the memo\n\t\t// selection...\n\t\tif (isMemoEdited) {\n\n\t\t\t// if the selection is remaining (or returning) to the\n\t\t\t// edited memo\n\t\t\t// then do nothing - may be returning to the edited memo\n\t\t\t// due to the\n\t\t\t// setSelectionInterval line\n\t\t\t// below that resets the selection\n\t\t\tif (editedMemoIndex == memoListTable.getSelectedRow())\n\t\t\t\treturn;\n\n\t\t\t// selection is moving to a new memo - prompt about\n\t\t\t// discarding\n\t\t\t// changes\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_Memo\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\n\t\t\t// if user does not want to lose changes, we need to set\n\t\t\t// the\n\t\t\t// selection back to the edited memo\n\t\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\t\tmemoListTable.getSelectionModel().setSelectionInterval(\n\t\t\t\t\t\teditedMemoIndex, editedMemoIndex);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tString memoName = getSelectedMemoName();\n\t\tif (memoName == null) {\n\t\t\tmemoText.setText(\"\");\n\t\t\tmemoText.setEditable(false);\n\t\t\tdateLabel.setText(\"\");\n\t\t\tencryptBox.setSelected(false);\n\t\t\tdecryptButton.setEnabled(false);\n\t\t} else {\n\n\t\t\t// show the selected memo\n\t\t\tString text;\n\t\t\ttry {\n\t\t\t\tMemo m = MemoModel.getReference().getMemo(memoName);\n\t\t\t\ttext = m.getMemoText();\n\n\t\t\t\t//\n\t\t\t\t// create the date label string\n\t\t\t\t//\n\t\t\t\tString datetext = Resource.getResourceString(\"created\") + \": \";\n\t\t\t\tif (m.getCreated() != null)\n\t\t\t\t\tdatetext += memoDateFormat.format(m.getCreated());\n\t\t\t\telse\n\t\t\t\t\tdatetext += Resource.getResourceString(\"unknown\");\n\t\t\t\tdatetext += \"           \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"updated\") + \": \";\n\t\t\t\tif (m.getUpdated() != null)\n\t\t\t\t\tdatetext += memoDateFormat.format(m.getUpdated());\n\t\t\t\telse\n\t\t\t\t\tdatetext += Resource.getResourceString(\"unknown\");\n\t\t\t\tdateLabel.setText(datetext);\n\n\t\t\t\tencryptBox.setSelected(m.isEncrypted());\n\t\t\t\tif (m.isEncrypted()) {\n\t\t\t\t\tmemoText.setText(Resource\n\t\t\t\t\t\t\t.getResourceString(\"EncryptedItem\"));\n\t\t\t\t\tmemoText.setEditable(false);\n\t\t\t\t\tdecryptButton.setEnabled(true);\n\t\t\t\t} else {\n\t\t\t\t\tmemoText.setEditable(true);\n\t\t\t\t\tmemoText.setText(text);\n\t\t\t\t\tdecryptButton.setEnabled(false);\n\n\t\t\t\t}\n\t\t\t\tmemoText.setCaretPosition(0);\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\t\tisMemoEdited = false;\n\t\tsaveButton.setEnabled(false);\n\n\t}\n\n\t@Override\n\tpublic JComponent getComponent() {\n\n\t\tif (!isInitialized) {\n\t\t\t// initialize UI\n\t\t\tinitializeComponents();\n\n\t\t\tmemoText.setEditable(false);\n\t\t\tsaveButton.setEnabled(false);\n\t\t\tdecryptButton.setEnabled(false);\n\n\t\t\tnew PopupMenuHelper(memoListTable,\n\t\t\t\t\tnew PopupMenuHelper.Entry[] { new PopupMenuHelper.Entry(\n\t\t\t\t\t\t\trenameAction, \"Rename\") });\n\n\t\t\trefresh();\n\n\t\t\t// listen for memo model changes\n\t\t\tMemoModel.getReference().addListener(this);\n\t\t\tisInitialized = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"Memos\");\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(\n\t\t\t\tIconHelper.getIcon(\"/resource/Edit16.gif\"),\n\t\t\t\tgetModuleName(), new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\t\t\tpar.setView(ViewType.MEMO);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.MEMO);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\ttry {\n\t\t\tString selectedMemo = getSelectedMemoName();\n\t\t\tif (selectedMemo == null)\n\t\t\t\treturn;\n\n\t\t\tthis.memoText.print(new MessageFormat(selectedMemo),\n\t\t\t\t\tnew MessageFormat(\"{0}\"));\n\t\t} catch (PrinterException e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\tprivate void clearEditFlag() {\n\t\tisMemoEdited = false;\n\t}\n\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.MEMO;\n\t}\n\n\t@Override\n\tpublic boolean canClose() {\n\t\tif (isMemoEdited) {\n\t\t\t/*\n\t\t\t * confirm discard of changes\n\t\t\t */\n\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\tResource.getResourceString(\"Edited_Memo\"),\n\t\t\t\t\tResource.getResourceString(\"Discard_Text?\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);\n\n\t\t\t// if user does not want to lose changes, we need to set\n\t\t\t// the\n\t\t\t// selection back to the edited memo\n\t\t\tif (ret != JOptionPane.OK_OPTION) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tisMemoEdited = false;\n\t\t\tthis.loadMemosFromModel();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * find action\n\t */\n\tprivate String searchString = null;\n\n\tprivate void doFind() {\n\n\t\t// current caret is the root of the search. search is forwards.\n\t\tint caretIndex = memoText.getCaretPosition();\n\n\t\t// prompt for search string - remember last string\n\t\tsearchString = JOptionPane.showInputDialog(null,\n\t\t\t\tResource.getResourceString(\"Search_For\"), searchString);\n\t\tif (searchString != null) {\n\n\t\t\t// search forwards\n\t\t\tint foundIndex = memoText.getPlainText().indexOf(searchString,\n\t\t\t\t\tcaretIndex);\n\t\t\tif (foundIndex != -1) {\n\t\t\t\t// highlight found text - this also moves caret to end of found\n\t\t\t\t// string as a side-effect,\n\t\t\t\t// which is what we want\n\t\t\t\tmemoText.select(foundIndex, foundIndex + searchString.length());\n\t\t\t} else {\n\t\t\t\t// indicate string not found - put caret back to top so next\n\t\t\t\t// search will\n\t\t\t\t// begin at the top\n\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\tResource.getResourceString(\"Not_Found_End\"));\n\t\t\t\tmemoText.setCaretPosition(0);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\t@Override\n\tpublic void cleanUp() {\n\t\t// on close - unselect memo\n\t\tmemoListTable.clearSelection();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/memo/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  UI for memos. As is stated elsewhere, the memo functionality is meant to be compatible\n  with the Palm Desktop Memo application so that Borg can sync with Palm devices. To remain compatible,\n  the Memo UI is relatively simplistic.\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/AppearanceOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport com.formdev.flatlaf.FlatLaf;\nimport com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;\nimport com.formdev.flatlaf.intellijthemes.FlatAllIJThemes.FlatIJLookAndFeelInfo;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport javax.swing.UIManager.LookAndFeelInfo;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.*;\n\n/**\n * Provides the UI for editing general appearance options\n */\npublic class AppearanceOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = -2799946173831477902L;\n\n\tprivate final JCheckBox canadabox;\n\n\tprivate final JCheckBox prioritySortBox;\n\n\tprivate JCheckBox doyBox = null;\n\n\tprivate final JCheckBox holiday1;\n\tprivate final JCheckBox iso8601Box = new JCheckBox();\n\n\tprivate final JComboBox<String> lnfBox;\n\tprivate final JComboBox<String> localebox;\n\n\tprivate final JCheckBox miltime;\n\tprivate final JCheckBox mondaycb;\n\n\tprivate JCheckBox truncbox = null;\n\tprivate final JComboBox<String> wkendhr;\n\n\tprivate final JComboBox<String> wkstarthr;\n\t\n\tprivate final JSpinner iconSizeSpinner = new JSpinner(new SpinnerNumberModel(16,8,64,1));\n\n\n\t/**\n\t * Instantiates a new appearance options panel.\n\t */\n\tpublic AppearanceOptionsPanel() {\n\n\t\t\n\t\tlnfBox = new JComboBox<String>();\n\t\tholiday1 = new JCheckBox();\n\t\tmondaycb = new JCheckBox();\n\t\tmiltime = new JCheckBox();\n\t\twkstarthr = new JComboBox<String>();\n\t\twkendhr = new JComboBox<String>();\n\t\tcanadabox = new JCheckBox();\n\t\tlocalebox = new JComboBox<String>();\n\t\tprioritySortBox = new JCheckBox();\n\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\tthis.setName(Resource.getResourceString(\"appearance\"));\n\t\t\n\t\tJLabel jLabel4 = new JLabel();\n\t\tResourceHelper.setText(jLabel4, \"Look_and_Feel:\");\n\t\tjLabel4.setLabelFor(lnfBox);\n\t\tthis.add(jLabel4,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tlnfBox.setEditable(false);\n\t\tlnfBox.setMaximumSize(new java.awt.Dimension(131, 24));\n\t\tlnfBox.setPreferredSize(new java.awt.Dimension(50, 24));\n\t\tlnfBox.setAutoscrolls(true);\n\t\tthis.add(lnfBox, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\n\t\tResourceHelper.setText(holiday1, \"Show_U.S._Holidays\");\n\t\tthis.add(holiday1,\n\t\t\t\tGridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(mondaycb, \"Week_Starts_with_Monday\");\n\t\tthis.add(mondaycb,\n\t\t\t\tGridBagConstraintsFactory.create(1, 5, GridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(miltime, \"Use_24_hour_time_format\");\n\t\tthis.add(miltime,\n\t\t\t\tGridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel5 = new JLabel();\n\t\tResourceHelper.setText(jLabel5, \"Week_View_Start_Hour:_\");\n\t\tjLabel5.setLabelFor(wkstarthr);\n\t\twkstarthr.setModel(new DefaultComboBoxModel<String>(new String[] { \"0\", \"1\",\n\t\t\t\t\"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\" }));\n\t\tthis.add(jLabel5,\n\t\t\t\tGridBagConstraintsFactory.create(0, 7, GridBagConstraints.BOTH));\n\n\t\twkendhr.setModel(new DefaultComboBoxModel<String>(new String[] { \"12\", \"13\",\n\t\t\t\t\"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\",\n\t\t\t\t\"24\" }));\n\t\tthis.add(wkstarthr,\n\t\t\t\tGridBagConstraintsFactory.create(1, 7, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel6 = new JLabel();\n\t\tResourceHelper.setText(jLabel6, \"Week_View_End_Hour:_\");\n\t\tjLabel6.setLabelFor(wkendhr);\n\t\tthis.add(wkendhr,\n\t\t\t\tGridBagConstraintsFactory.create(1, 8, GridBagConstraints.BOTH));\n\t\tthis.add(jLabel6,\n\t\t\t\tGridBagConstraintsFactory.create(0, 8, GridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(canadabox, \"Show_Canadian_Holidays\");\n\t\tthis.add(canadabox,\n\t\t\t\tGridBagConstraintsFactory.create(1, 4, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel8 = new JLabel();\n\t\tResourceHelper.setText(jLabel8, \"locale\");\n\t\tjLabel8.setLabelFor(localebox);\n\t\tthis.add(jLabel8, GridBagConstraintsFactory.create(0, 12,\n\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tthis.add(localebox, GridBagConstraintsFactory.create(1, 12,\n\t\t\t\tGridBagConstraints.BOTH));\n\t\t\n\t\t\n\t\tthis.add(new JLabel(Resource.getResourceString(\"iconSize\")),GridBagConstraintsFactory.create(0, 13,\n\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tthis.add(iconSizeSpinner, GridBagConstraintsFactory.create(1, 13,\n\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(iso8601Box, \"ISO_week_number\");\n\t\tthis.add(iso8601Box, GridBagConstraintsFactory.create(0, 9,\n\t\t\t\tGridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\tResourceHelper.setText(prioritySortBox, \"sort_by_priority\");\n\t\tthis.add(prioritySortBox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH));\n\n\t\tdoyBox = new JCheckBox();\n\t\tResourceHelper.setText(doyBox, \"showdoy\");\n\t\tthis.add(doyBox,\n\t\t\t\tGridBagConstraintsFactory.create(1, 6, GridBagConstraints.BOTH));\n\n\t\ttruncbox = new JCheckBox();\n\t\tResourceHelper.setText(truncbox, \"truncate_appts\");\n\t\tthis.add(truncbox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n\n\t\tlnfBox.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tlnfChanged();\n\t\t\t}\n\n\t\t});\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\n\t\n\t\tOptionsPanel.setBooleanPref(holiday1, PrefName.SHOWUSHOLIDAYS);\n\t\tOptionsPanel.setBooleanPref(canadabox, PrefName.SHOWCANHOLIDAYS);\n\t\tOptionsPanel.setBooleanPref(doyBox, PrefName.DAYOFYEAR);\n\t\tOptionsPanel.setBooleanPref(prioritySortBox, PrefName.PRIORITY_SORT);\n\t\tOptionsPanel.setBooleanPref(miltime, PrefName.MILTIME);\n\t\tOptionsPanel.setBooleanPref(truncbox, PrefName.TRUNCAPPT);\n\t\tOptionsPanel.setBooleanPref(iso8601Box, PrefName.ISOWKNUMBER);\n\t\t\n\n\t\t// first day of week - either monday or sunday\n\t\tif (mondaycb.isSelected()) {\n\t\t\tPrefs.putPref(PrefName.FIRSTDOW, Integer.valueOf(Calendar.MONDAY));\n\t\t} else {\n\t\t\tPrefs.putPref(PrefName.FIRSTDOW, Integer.valueOf(Calendar.SUNDAY));\n\t\t}\n\n\t\tPrefs.putPref(PrefName.WKENDHOUR, wkendhr.getSelectedItem());\n\t\tPrefs.putPref(PrefName.WKSTARTHOUR, wkstarthr.getSelectedItem());\n\n\t\t// locale\n\t\tLocale[] locs = Locale.getAvailableLocales();\n\t\tString choice = (String) localebox.getSelectedItem();\n\t\tfor (int ii = 0; ii < locs.length; ii++) {\n\t\t\tif (choice.equals(locs[ii].getDisplayName())) {\n\t\t\t\tPrefs.putPref(PrefName.COUNTRY, locs[ii].getCountry());\n\t\t\t\tPrefs.putPref(PrefName.LANGUAGE, locs[ii].getLanguage());\n\t\t\t}\n\t\t}\n\t\t\n\t\tInteger iconSize = (Integer) iconSizeSpinner.getValue();\n\t\tPrefs.putPref(PrefName.ICONSIZE, iconSize);\n\n\t\t// look and feel\n\t\t// we no longer restart automatically or attempt to update the lnf\n\t\t// while running\n\t\tboolean lnfChange = false;\n\t\tString newlnf = (String) lnfBox.getSelectedItem();\n\t\tString oldlnf = Prefs.getPref(PrefName.LNF);\n\t\tif (!newlnf.equals(oldlnf)) {\n\t\t\tlnfChange = true;\n\t\t\tPrefs.putPref(PrefName.LNF, newlnf);\n\t\t}\n\n\n\t\tif (lnfChange) {\n\t\t\ttry {\n\t\t\tUIManager.setLookAndFeel(newlnf);\n\t\t\tFlatLaf.updateUI();\n\t\t\t}\n\t\t\tcatch( Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t\t\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\t\n\t\tOptionsPanel.setCheckBox(holiday1, PrefName.SHOWUSHOLIDAYS);\n\t\tOptionsPanel.setCheckBox(canadabox, PrefName.SHOWCANHOLIDAYS);\n\t\tOptionsPanel.setCheckBox(doyBox, PrefName.DAYOFYEAR);\n\t\tOptionsPanel.setCheckBox(prioritySortBox, PrefName.PRIORITY_SORT);\n\t\tOptionsPanel.setCheckBox(miltime, PrefName.MILTIME);\n\n\t\tOptionsPanel.setCheckBox(truncbox, PrefName.TRUNCAPPT);\n\t\tOptionsPanel.setCheckBox(iso8601Box, PrefName.ISOWKNUMBER);\n\n\t\t// monday first day of week option\n\t\tint fdow = Prefs.getIntPref(PrefName.FIRSTDOW);\n\t\tmondaycb.setSelected(fdow == Calendar.MONDAY);\n\n\t\t// start and end hour for the time grids\n\t\tString shr = Prefs.getPref(PrefName.WKSTARTHOUR);\n\t\tString ehr = Prefs.getPref(PrefName.WKENDHOUR);\n\t\twkstarthr.setSelectedItem(shr);\n\t\twkendhr.setSelectedItem(ehr);\n\n\t\t// add locales\n\t\tlocalebox.removeAllItems();\n\t\tLocale[] locs = Locale.getAvailableLocales();\n\t\tArrayList<Locale> loclist = new ArrayList<Locale>();\n\t\tCollections.addAll(loclist, locs);\n\t\tComparator<Locale> comparator = new Comparator<Locale>() {\n\n\t        @Override\n\t        public int compare(Locale o1, Locale o2) {\n\t            return o1.getDisplayName().compareTo(o2.getDisplayName());\n\t        }\n\t    };\n\t\tCollections.sort(loclist, comparator);\n\t\tfor (Locale locale : loclist) {\n\t\t\tlocalebox.addItem(locale.getDisplayName());\n\t\t}\n\n\t\tString currentlocale = Locale.getDefault().getDisplayName();\n\t\tlocalebox.setSelectedItem(currentlocale);\n\t\t\n\t\tint iconSize = Prefs.getIntPref(PrefName.ICONSIZE);\n\t\ticonSizeSpinner.setValue(Integer.valueOf(iconSize));\n\n\n\t\t// add installed look and feels to lnfBox\n\t\tlnfBox.removeAllItems();\n\t\tCollection<String> lnfs = new TreeSet<String>();\n\t\tString curlnf = Prefs.getPref(PrefName.LNF);\n\n\t\t// add installed JRE look and feels\n\t\tLookAndFeelInfo[] lnfinfo = UIManager.getInstalledLookAndFeels();\n\t\tfor (int i = 0; i < lnfinfo.length; i++) {\n\t\t\tString name = lnfinfo[i].getClassName();\n\t\t\tlnfs.add(name);\n\t\t}\n\n\t\t\n\t\t// add some included look and feels\n\t\tString[] looks = { \n\t\t\t\t\"com.formdev.flatlaf.FlatLightLaf\",\n\t\t\t\t\"com.formdev.flatlaf.FlatDarkLaf\"\n\t\t\t\t};\n\n\t\tfor (String look : looks) {\n\t\t\ttry {\n\t\t\t\tClass.forName(look);\n\t\t\t\tlnfs.add(look);\n\t\t\t} catch (Throwable e) {\n\t\t\t\t// empty\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor( FlatIJLookAndFeelInfo ijInfo : FlatAllIJThemes.INFOS )\n\t\t{\n\t\t\tlnfs.add(ijInfo.getClassName());\n\t\t}\n\n\t\t// add the look and feel in the preference store\n\t\tlnfs.add(curlnf);\n\n\t\t// add to the combo box\n\t\tfor (String lnf : lnfs) {\n\t\t\tlnfBox.addItem(lnf);\n\t\t}\n\n\t\tlnfBox.setSelectedItem(curlnf);\n\t\tlnfBox.setEditable(false);\n\n\t\n\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"appearance\");\n\t}\n\n\tprivate void lnfChanged() {\n\t\tString lnf = (String) lnfBox.getSelectedItem();\n\t\tif (lnf == null)\n\t\t\treturn;\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/ColorOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2011 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.Collection;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.ColorChooserButton;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * Provides the UI for editing color options\n */\npublic class ColorOptionsPanel extends OptionsPanel implements Prefs.Listener {\n\n\tprivate static final long serialVersionUID = 2184312216077136324L;\n\n\t/*\n\t * the user color scheme buttons that let the user set colors\n\t */\n\tprivate final ColorChooserButton btn_ucs_birthdays;\n\tprivate final ColorChooserButton btn_ucs_black;\n\tprivate final ColorChooserButton btn_ucs_blue;\n\tprivate final ColorChooserButton btn_ucs_defaultBg;\n\tprivate final ColorChooserButton btn_ucs_defaultFg;\n\tprivate final ColorChooserButton btn_ucs_green;\n\tprivate final ColorChooserButton btn_ucs_halfday;\n\tprivate final ColorChooserButton btn_ucs_holiday;\n\tprivate final ColorChooserButton btn_ucs_holidays;\n\tprivate final ColorChooserButton btn_ucs_red;\n\tprivate final ColorChooserButton btn_ucs_reminderBg;\n\tprivate final ColorChooserButton btn_ucs_stripe;\n\tprivate final ColorChooserButton btn_ucs_tasks;\n\tprivate final ColorChooserButton btn_ucs_today;\n\tprivate final ColorChooserButton btn_ucs_vacation;\n\tprivate final ColorChooserButton btn_ucs_weekday;\n\tprivate final ColorChooserButton btn_ucs_weekend;\n\tprivate final ColorChooserButton btn_ucs_white;\n\tprivate final ColorChooserButton btn_tray_fg;\n\tprivate final ColorChooserButton btn_tray_bg;\n\n\tprivate final JCheckBox cb_ucs_marktodo;\n\tprivate final JCheckBox cb_ucs_ontodo;\n\tprivate final JCheckBox gradientApptBox = new JCheckBox();\n\tprivate final JCheckBox pillBox = new JCheckBox();\n\tprivate final JTextField tf_ucs_marker;\n\n\tprivate final JComboBox<String> themeChooser = new JComboBox<String>();\n\n\t/**\n\t * Instantiates a new color options panel.\n\t */\n\tpublic ColorOptionsPanel() {\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\tJPanel themePanel = new JPanel();\n\t\tthemePanel.setLayout(new GridBagLayout());\n\n\t\tthemePanel.add(new JLabel(Resource.getResourceString(\"Theme\") + \":\"), GridBagConstraintsFactory.create(0, 0));\n\n\t\tthemeChooser.setEditable(false);\n\t\tthemePanel.add(themeChooser, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tthemeChooser.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t// switch the displayed theme, but not the active (current)\n\t\t\t\t// theme\n\t\t\t\tString name = (String) themeChooser.getSelectedItem();\n\t\t\t\tif (name == null)\n\t\t\t\t\treturn;\n\t\t\t\tTheme t = Theme.getTheme(name);\n\t\t\t\tshowTheme(t);\n\t\t\t}\n\n\t\t});\n\n\t\tJButton saveButton = new JButton();\n\t\tsaveButton.setIcon(IconHelper.getIcon(\"/resource/Add16.gif\"));\n\t\tsaveButton.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\n\t\t\t\t// create a new theme and store in the db\n\t\t\t\tString name = JOptionPane.showInputDialog(Resource.getResourceString(\"New_Theme_Name\"));\n\t\t\t\tif (name == null)\n\t\t\t\t\treturn;\n\n\t\t\t\tTheme newTheme = new Theme();\n\t\t\t\tsetTheme(newTheme); // set the new theme from based on the\n\t\t\t\t\t\t\t\t\t// currently displayed theme\n\t\t\t\tnewTheme.setName(name);\n\t\t\t\ttry {\n\t\t\t\t\t// save the theme\n\t\t\t\t\tnewTheme.save();\n\t\t\t\t\tthemeChooser.addItem(name);\n\t\t\t\t\tthemeChooser.setSelectedItem(name);\n\t\t\t\t} catch (Warning e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(e1.getLocalizedMessage());\n\t\t\t\t\treturn;\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\n\t\tthemePanel.add(saveButton, GridBagConstraintsFactory.create(2, 0));\n\n\t\tJButton delButton = new JButton();\n\t\tdelButton.setIcon(IconHelper.getIcon(\"/resource/Delete16.gif\"));\n\t\tdelButton.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t// delete the selected theme\n\t\t\t\ttry {\n\t\t\t\t\tTheme.delete((String) themeChooser.getSelectedItem());\n\t\t\t\t\tloadThemes();\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t\tthemePanel.add(delButton, GridBagConstraintsFactory.create(3, 0));\n\n\t\tthis.add(themePanel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tJPanel colorPanel = new JPanel();\n\t\tcolorPanel.setLayout(new GridLayout(13, 2));\n\n\t\tcb_ucs_ontodo = new JCheckBox();\n\t\tResourceHelper.setText(cb_ucs_ontodo, \"ucolortext1\");\n\t\tcb_ucs_marktodo = new JCheckBox();\n\t\tResourceHelper.setText(cb_ucs_marktodo, \"ucolortext2\");\n\t\ttf_ucs_marker = new JTextField(\"! \");\n\t\tbtn_ucs_red = new ColorChooserButton(Resource.getResourceString(\"ucolortext4\"), Color.WHITE);\n\t\tbtn_ucs_blue = new ColorChooserButton(Resource.getResourceString(\"ucolortext5\"), Color.WHITE);\n\t\tbtn_ucs_green = new ColorChooserButton(Resource.getResourceString(\"ucolortext6\"), Color.WHITE);\n\t\tbtn_ucs_black = new ColorChooserButton(Resource.getResourceString(\"ucolortext7\"), Color.WHITE);\n\t\tbtn_ucs_white = new ColorChooserButton(Resource.getResourceString(\"ucolortext8\"), Color.WHITE);\n\t\tbtn_ucs_tasks = new ColorChooserButton(Resource.getResourceString(\"ucolortext9\"), Color.WHITE);\n\t\tbtn_ucs_holidays = new ColorChooserButton(Resource.getResourceString(\"ucolortext10\"), Color.WHITE);\n\t\tbtn_ucs_birthdays = new ColorChooserButton(Resource.getResourceString(\"ucolortext11\"), Color.WHITE);\n\t\tbtn_ucs_defaultBg = new ColorChooserButton(Resource.getResourceString(\"ucolortext12\"), Color.WHITE);\n\t\tbtn_ucs_defaultFg = new ColorChooserButton(Resource.getResourceString(\"defaultfg\"), Color.WHITE);\n\t\tbtn_ucs_holiday = new ColorChooserButton(Resource.getResourceString(\"ucolortext13\"), Color.WHITE);\n\t\tbtn_ucs_halfday = new ColorChooserButton(Resource.getResourceString(\"ucolortext14\"), Color.WHITE);\n\t\tbtn_ucs_vacation = new ColorChooserButton(Resource.getResourceString(\"ucolortext15\"), Color.WHITE);\n\t\tbtn_ucs_today = new ColorChooserButton(Resource.getResourceString(\"ucolortext16\"), Color.WHITE);\n\t\tbtn_ucs_weekend = new ColorChooserButton(Resource.getResourceString(\"ucolortext17\"), Color.WHITE);\n\t\tbtn_ucs_weekday = new ColorChooserButton(Resource.getResourceString(\"ucolortext18\"), Color.WHITE);\n\t\tbtn_ucs_stripe = new ColorChooserButton(Resource.getResourceString(\"stripecolor\"), Color.WHITE);\n\t\tbtn_tray_fg = new ColorChooserButton(Resource.getResourceString(\"tray_fg\"), Color.WHITE);\n\t\tbtn_tray_bg = new ColorChooserButton(Resource.getResourceString(\"tray_bg\"), Color.WHITE);\n\t\tbtn_ucs_reminderBg = new ColorChooserButton(Resource.getResourceString(\"reminder_bg\"), Color.WHITE);\n\t\tJButton btn_ucs_restore = new JButton(Resource.getResourceString(\"restore_defaults\"));\n\n\t\tbtn_ucs_restore.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tTheme t = Theme.getDefaults((String) themeChooser.getSelectedItem());\n\n\t\t\t\tbtn_ucs_red.setColorProperty(new Color(t.getTextColor1()));\n\t\t\t\tbtn_ucs_blue.setColorProperty(new Color(t.getTextColor2()));\n\t\t\t\tbtn_ucs_green.setColorProperty(new Color(t.getTextColor3()));\n\t\t\t\tbtn_ucs_black.setColorProperty(new Color(t.getTextColor4()));\n\t\t\t\tbtn_ucs_white.setColorProperty(new Color(t.getTextColor5()));\n\t\t\t\tbtn_ucs_tasks.setColorProperty(new Color(t.getTaskTextColor()));\n\t\t\t\tbtn_ucs_holidays.setColorProperty(new Color(t.getHolidayTextColor()));\n\t\t\t\tbtn_ucs_birthdays.setColorProperty(new Color(t.getBirthdayTextColor()));\n\t\t\t\tbtn_ucs_defaultBg.setColorProperty(new Color(t.getDefaultBg()));\n\t\t\t\tbtn_ucs_defaultFg.setColorProperty(new Color(t.getDefaultFg()));\n\t\t\t\tbtn_ucs_today.setColorProperty(new Color(t.getTodayBg()));\n\t\t\t\tbtn_ucs_holiday.setColorProperty(new Color(t.getHolidayBg()));\n\t\t\t\tbtn_ucs_vacation.setColorProperty(new Color(t.getVacationBg()));\n\t\t\t\tbtn_ucs_halfday.setColorProperty(new Color(t.getHalfdayBg()));\n\t\t\t\tbtn_ucs_weekend.setColorProperty(new Color(t.getWeekendBg()));\n\t\t\t\tbtn_ucs_weekday.setColorProperty(new Color(t.getWeekdayBg()));\n\t\t\t\tbtn_tray_fg.setColorProperty(new Color(t.getTrayIconFg()));\n\t\t\t\tbtn_tray_bg.setColorProperty(new Color(t.getTrayIconBg()));\n\t\t\t\tbtn_ucs_reminderBg.setColorProperty(new Color(t.getReminderBg()));\n\t\t\t}\n\t\t});\n\n\t\tcolorPanel.add(btn_ucs_red);\n\t\tcolorPanel.add(btn_ucs_defaultBg);\n\t\tcolorPanel.add(btn_ucs_defaultFg);\n\t\tcolorPanel.add(btn_ucs_blue);\n\t\tcolorPanel.add(btn_ucs_today);\n\t\tcolorPanel.add(btn_ucs_green);\n\t\tcolorPanel.add(btn_ucs_holiday);\n\t\tcolorPanel.add(btn_ucs_black);\n\t\tcolorPanel.add(btn_ucs_halfday);\n\t\tcolorPanel.add(btn_ucs_white);\n\t\tcolorPanel.add(btn_ucs_vacation);\n\t\tcolorPanel.add(btn_ucs_tasks);\n\t\tcolorPanel.add(btn_ucs_weekend);\n\t\tcolorPanel.add(btn_ucs_holidays);\n\t\tcolorPanel.add(btn_ucs_weekday);\n\t\tcolorPanel.add(btn_ucs_birthdays);\n\t\tcolorPanel.add(btn_ucs_stripe);\n\t\tcolorPanel.add(btn_tray_fg);\n\t\tcolorPanel.add(btn_tray_bg);\n\t\tcolorPanel.add(btn_ucs_reminderBg);\n\t\tpillBox.setText(Resource.getResourceString(\"draw_background_pills\"));\n\t\tcolorPanel.add(pillBox);\n\t\tcolorPanel.add(btn_ucs_restore);\n\t\tcolorPanel.add(cb_ucs_ontodo);\n\n\t\tJPanel njp = new JPanel();\n\t\tnjp.setLayout(new BorderLayout());\n\t\tnjp.add(cb_ucs_marktodo, BorderLayout.WEST);\n\t\tnjp.add(tf_ucs_marker, BorderLayout.CENTER);\n\t\tcolorPanel.add(njp);\n\n\t\tgradientApptBox.setText(Resource.getResourceString(\"gradient_appts\"));\n\t\tcolorPanel.add(gradientApptBox);\n\n\t\tthis.add(colorPanel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\tloadThemes();\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tOptionsPanel.setBooleanPref(gradientApptBox, PrefName.GRADIENT_APPTS);\n\t\tOptionsPanel.setBooleanPref(cb_ucs_ontodo, PrefName.UCS_ONTODO);\n\t\tOptionsPanel.setBooleanPref(cb_ucs_marktodo, PrefName.UCS_MARKTODO);\n\n\t\tPrefs.putPref(PrefName.UCS_MARKER, tf_ucs_marker.getText());\n\n\t\t/*\n\t\t * When changes are applied, save the current theme and make the currently shown\n\t\t * theme active\n\t\t */\n\t\tTheme t = new Theme();\n\t\tsetTheme(t);\n\n\t\ttry {\n\t\t\t// don't update to the db if no changes\n\t\t\tTheme orig = Theme.getTheme(t.getName());\n\t\t\tif (!t.equals(orig)) {\n\t\t\t\tt.save();\n\t\t\t}\n\t\t\tTheme.setCurrentTheme(t);\n\t\t\tloadThemes();\n\t\t} catch (Warning e1) {\n\t\t\tErrmsg.getErrorHandler().notice(e1.getLocalizedMessage());\n\t\t\treturn;\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\treturn;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"UserColorScheme\");\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\n\t\tloadThemes();\n\n\t\tOptionsPanel.setCheckBox(gradientApptBox, PrefName.GRADIENT_APPTS);\n\t\tOptionsPanel.setCheckBox(cb_ucs_ontodo, PrefName.UCS_ONTODO);\n\t\tOptionsPanel.setCheckBox(cb_ucs_marktodo, PrefName.UCS_MARKTODO);\n\t\ttf_ucs_marker.setText(Prefs.getPref(PrefName.UCS_MARKER));\n\n\t}\n\n\t/**\n\t * clear the theme chooser and load all themes as chooser options. show the\n\t * active theme\n\t */\n\tprivate void loadThemes() {\n\t\tthemeChooser.removeAllItems();\n\t\tCollection<String> themeNames = Theme.getThemeNames();\n\n\t\tfor (String name : themeNames) {\n\t\t\tthemeChooser.addItem(name);\n\t\t}\n\n\t\tTheme t = Theme.getCurrentTheme();\n\n\t\tthemeChooser.setSelectedItem(t.getName());\n\t\tshowTheme(t);\n\n\t}\n\n\t/**\n\t * set the data items in a theme object from the values shown on the UI\n\t * \n\t * @param t - the Theme to be set\n\t */\n\tprivate void setTheme(Theme t) {\n\t\tt.setName((String) themeChooser.getSelectedItem());\n\n\t\tt.setTextColor1(btn_ucs_red.getColorProperty().getRGB());\n\t\tt.setTextColor2(btn_ucs_blue.getColorProperty().getRGB());\n\t\tt.setTextColor3(btn_ucs_green.getColorProperty().getRGB());\n\t\tt.setTextColor4(btn_ucs_black.getColorProperty().getRGB());\n\t\tt.setTextColor5(btn_ucs_white.getColorProperty().getRGB());\n\n\t\tt.setTaskTextColor(btn_ucs_tasks.getColorProperty().getRGB());\n\t\tt.setHolidayTextColor(btn_ucs_holidays.getColorProperty().getRGB());\n\t\tt.setBirthdayTextColor(btn_ucs_birthdays.getColorProperty().getRGB());\n\n\t\tt.setDefaultBg(btn_ucs_defaultBg.getColorProperty().getRGB());\n\t\tt.setDefaultFg(btn_ucs_defaultFg.getColorProperty().getRGB());\n\t\tt.setHolidayBg(btn_ucs_holiday.getColorProperty().getRGB());\n\t\tt.setHalfdayBg(btn_ucs_halfday.getColorProperty().getRGB());\n\t\tt.setVacationBg(btn_ucs_vacation.getColorProperty().getRGB());\n\n\t\tt.setTodayBg(btn_ucs_today.getColorProperty().getRGB());\n\t\tt.setWeekendBg(btn_ucs_weekend.getColorProperty().getRGB());\n\t\tt.setWeekdayBg(btn_ucs_weekday.getColorProperty().getRGB());\n\t\tt.setStripeBg(btn_ucs_stripe.getColorProperty().getRGB());\n\t\tt.setTrayIconFg(btn_tray_fg.getColorProperty().getRGB());\n\t\tt.setTrayIconBg(btn_tray_bg.getColorProperty().getRGB());\n\t\tt.setReminderBg(btn_ucs_reminderBg.getColorProperty().getRGB());\n\t\tt.setPills(pillBox.isSelected());\n\n\t}\n\n\t/**\n\t * set the UI from the values in a Theme\n\t * \n\t * @param t - the Theme to show on the UI\n\t */\n\tprivate void showTheme(Theme t) {\n\t\tbtn_ucs_red.setColorProperty(new Color(t.getTextColor1()));\n\t\tbtn_ucs_blue.setColorProperty(new Color(t.getTextColor2()));\n\t\tbtn_ucs_green.setColorProperty(new Color(t.getTextColor3()));\n\t\tbtn_ucs_black.setColorProperty(new Color(t.getTextColor4()));\n\t\tbtn_ucs_white.setColorProperty(new Color(t.getTextColor5()));\n\n\t\tbtn_ucs_tasks.setColorProperty(new Color(t.getTaskTextColor()));\n\t\tbtn_ucs_holidays.setColorProperty(new Color(t.getHolidayTextColor()));\n\t\tbtn_ucs_birthdays.setColorProperty(new Color(t.getBirthdayTextColor()));\n\n\t\tbtn_ucs_defaultBg.setColorProperty(new Color(t.getDefaultBg()));\n\t\tbtn_ucs_defaultFg.setColorProperty(new Color(t.getDefaultFg()));\n\t\tbtn_ucs_today.setColorProperty(new Color(t.getTodayBg()));\n\t\tbtn_ucs_holiday.setColorProperty(new Color(t.getHolidayBg()));\n\t\tbtn_ucs_halfday.setColorProperty(new Color(t.getHalfdayBg()));\n\t\tbtn_ucs_vacation.setColorProperty(new Color(t.getVacationBg()));\n\t\tbtn_ucs_weekend.setColorProperty(new Color(t.getWeekendBg()));\n\t\tbtn_ucs_weekday.setColorProperty(new Color(t.getWeekdayBg()));\n\t\tbtn_ucs_stripe.setColorProperty(new Color(t.getStripeBg()));\n\t\tbtn_tray_fg.setColorProperty(new Color(t.getTrayIconFg()));\n\t\tbtn_tray_bg.setColorProperty(new Color(t.getTrayIconBg()));\n\t\tbtn_ucs_reminderBg.setColorProperty(new Color(t.getReminderBg()));\n\t\tpillBox.setSelected(t.isPills());\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\t\tloadThemes();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/DatabaseOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.JTextField;\nimport javax.swing.SwingConstants;\nimport javax.swing.border.TitledBorder;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.HelpLauncher;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * Provides the UI for editing database options\n */\npublic class DatabaseOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = 7695315940556870216L;\n\n\tprivate final JTextField dbDirText;\n\n\tprivate ButtonGroup dbTypeGroup;\n\n\tprivate JPanel dbTypePanel = null;\n\n\tprivate JRadioButton h2Button;\n\tprivate JRadioButton sqliteButton;\n\n\tprivate final JTextField h2dir = new JTextField();\n\tprivate final JTextField sqlitedir = new JTextField();\n\n\tprivate JPanel h2Panel;\n\tprivate JPanel sqlitePanel;\n\n\tprivate JRadioButton jdbcButton = null;\n\n\tprivate JPanel jdbcPanel = null;\n\n\tprivate JTextField jdbcText = null;\n\n\t/**\n\t * Instantiates a new database options panel.\n\t */\n\tpublic DatabaseOptionsPanel() {\n\n\t\tdbDirText = new JTextField();\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\tGridBagConstraints gbcm = GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tgbcm.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tthis.add(getDbTypePanel(), GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tGridBagConstraints gridBagConstraints5 = GridBagConstraintsFactory.create(0, 8);\n\t\tgridBagConstraints5.weightx = 1.0;\n\t\tgridBagConstraints5.anchor = GridBagConstraints.CENTER;\n\t\tJButton chgdb = new JButton();\n\t\tthis.add(chgdb, gridBagConstraints5);\n\t\tchgdb.setForeground(new java.awt.Color(255, 0, 51));\n\t\tchgdb.setIcon(IconHelper.getIcon(\"/resource/Refresh16.gif\"));\n\t\tResourceHelper.setText(chgdb, \"Apply_DB_Change\");\n\t\tchgdb.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tchgdbActionPerformed();\n\t\t\t}\n\t\t});\n\n\t\tJButton help = new JButton();\n\t\tGridBagConstraints gridBagConstraintsh = GridBagConstraintsFactory.create(1, 8);\n\t\tgridBagConstraintsh.weightx = 1.0;\n\t\tgridBagConstraintsh.anchor = GridBagConstraints.CENTER;\n\t\tthis.add(help, gridBagConstraintsh);\n\t\thelp.setForeground(new java.awt.Color(255, 0, 51));\n\t\thelp.setIcon(IconHelper.getIcon(\"/resource/Help16.gif\"));\n\t\tResourceHelper.setText(help, \"Help\");\n\n\t\thelp.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tHelpLauncher.launchHelp();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t\n\t\tGridBagConstraints gridBagConstraints7h = GridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH, 1.0,\n\t\t\t\t1.0);\n\t\tgridBagConstraints7h.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tthis.add(getJdbcPanel(), gridBagConstraints7h);\n\n\t\tGridBagConstraints gridBagConstraints8h = GridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH, 1.0,\n\t\t\t\t1.0);\n\t\tgridBagConstraints8h.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tthis.add(getH2Panel(), gridBagConstraints8h);\n\n\t\tGridBagConstraints gridBagConstraints9h = GridBagConstraintsFactory.create(0, 7, GridBagConstraints.BOTH, 1.0,\n\t\t\t\t1.0);\n\t\tgridBagConstraints9h.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tthis.add(getSqlitePanel(), gridBagConstraints9h);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\t// empty - db options only applied when chg db button pressed\n\t}\n\n\t/**\n\t * change the database settings - will update the settings and then restart the\n\t * program\n\t */\n\tprivate void chgdbActionPerformed() {\n\t\tint ret = JOptionPane.showConfirmDialog(null, Resource.getResourceString(\"Really_change_the_database?\"),\n\t\t\t\tResource.getResourceString(\"Confirm_DB_Change\"), JOptionPane.YES_NO_OPTION);\n\t\tif (ret == JOptionPane.YES_OPTION) {\n\n\t\t\tString h2 = h2dir.getText();\n\t\t\tPrefs.putPref(PrefName.H2DIR, h2);\n\t\t\t\n\t\t\tString sq = sqlitedir.getText();\n\t\t\tPrefs.putPref(PrefName.SQLITEDIR, sq);\n\n\t\t\tif (h2Button.isSelected()) {\n\t\t\t\tPrefs.putPref(PrefName.DBTYPE, \"h2\");\n\t\t\t} else if( sqliteButton.isSelected()) {\n\t\t\t\tPrefs.putPref(PrefName.DBTYPE, \"sqlite\");\n\t\t\t} else {\n\t\t\t\tPrefs.putPref(PrefName.DBTYPE, \"jdbc\");\n\t\t\t}\n\n\t\t\tPrefs.putPref(PrefName.JDBCURL, jdbcText.getText());\n\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"Restart_Warning\"));\n\t\t\tSystem.exit(0);\n\t\t}\n\t}\n\n\t/**\n\t * show the db settings panel that corresponds to the chosen db type and hide\n\t * the other panels\n\t *\n\t * @param type db type\n\t */\n\tprivate void dbTypeChange(String type) {\n\t\t if (type.equals(\"h2\")) {\n\t\t\tjdbcPanel.setVisible(false);\n\t\t\th2Panel.setVisible(true);\n\t\t\tsqlitePanel.setVisible(false);\n\t\t} else if (type.equals(\"sqlite\")) {\n\t\t\tsqlitePanel.setVisible(true);\n\t\t\tjdbcPanel.setVisible(false);\n\t\t\th2Panel.setVisible(false);\n\t\t} else {\n\t\t\tjdbcPanel.setVisible(true);\n\t\t\th2Panel.setVisible(false);\n\t\t\tsqlitePanel.setVisible(false);\n\t\t}\n\t}\n\n\t/**\n\t * get the database type button panel\n\t *\n\t * @return the database type button panel\n\t */\n\tprivate JPanel getDbTypePanel() {\n\t\tif (dbTypePanel == null) {\n\t\t\tFlowLayout flowLayout = new FlowLayout();\n\t\t\tflowLayout.setAlignment(java.awt.FlowLayout.LEFT);\n\t\t\tflowLayout.setHgap(40);\n\t\t\tdbTypePanel = new JPanel();\n\t\t\tdbTypePanel.setLayout(flowLayout);\n\t\t\t\n\t\t\tsqliteButton = new JRadioButton();\n\t\t\tsqliteButton.setActionCommand(\"sqlite\");\n\t\t\tsqliteButton.setText(\"SQLITE\");\n\t\t\tsqliteButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tdbTypeChange(e.getActionCommand());\n\t\t\t\t}\n\t\t\t});\n\t\t\tdbTypePanel.add(sqliteButton, null);\n\n\t\t\th2Button = new JRadioButton();\n\t\t\th2Button.setActionCommand(\"h2\");\n\t\t\th2Button.setText(\"H2\");\n\t\t\th2Button.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tdbTypeChange(e.getActionCommand());\n\t\t\t\t}\n\t\t\t});\n\t\t\tdbTypePanel.add(h2Button, null);\n\n\t\t\n\n\t\t\tjdbcButton = new JRadioButton();\n\t\t\tjdbcButton.setActionCommand(\"jdbc\");\n\t\t\tResourceHelper.setText(jdbcButton, \"jdbc\");\n\t\t\tjdbcButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tdbTypeChange(e.getActionCommand());\n\t\t\t\t}\n\t\t\t});\n\t\t\tdbTypePanel.add(jdbcButton, null);\n\n\t\t\tdbTypeGroup = new ButtonGroup();\n\t\t\tdbTypeGroup.add(sqliteButton);\n\t\t\tdbTypeGroup.add(h2Button);\n\t\t\tdbTypeGroup.add(jdbcButton);\n\n\t\t}\n\t\treturn dbTypePanel;\n\t}\n\n\n\n\t/**\n\t * get the h2 options panel\n\t *\n\t * @return the h2 options panel\n\t */\n\tprivate JPanel getH2Panel() {\n\t\th2Panel = new JPanel();\n\t\th2Panel.setLayout(new java.awt.GridBagLayout());\n\n\t\tJLabel hs1 = new JLabel();\n\t\th2Panel.setBorder(new TitledBorder(Resource.getResourceString(\"h2info\")));\n\t\tResourceHelper.setText(hs1, \"DataBase_Directory\");\n\t\ths1.setLabelFor(dbDirText);\n\t\th2Panel.add(hs1, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\th2Panel.add(h2dir, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 0.5, 0.0));\n\n\t\tJButton hsb1 = new JButton();\n\t\tResourceHelper.setText(hsb1, \"Browse\");\n\t\thsb1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t// browse for new database dir\n\t\t\t\tString dbdir = OptionsPanel.chooseDir();\n\t\t\t\tif (dbdir == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// update text field - nothing else changes. DB change will take\n\t\t\t\t// effect\n\t\t\t\t// only on restart\n\t\t\t\th2dir.setText(dbdir);\n\t\t\t}\n\t\t});\n\n\t\th2Panel.add(hsb1, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH));\n\n\t\treturn h2Panel;\n\t}\n\n\t/**\n\t * get the h2 options panel\n\t *\n\t * @return the h2 options panel\n\t */\n\tprivate JPanel getSqlitePanel() {\n\t\tsqlitePanel = new JPanel();\n\t\tsqlitePanel.setLayout(new java.awt.GridBagLayout());\n\n\t\tJLabel sql1 = new JLabel();\n\t\tsqlitePanel.setBorder(new TitledBorder(Resource.getResourceString(\"sqliteinfo\")));\n\t\tResourceHelper.setText(sql1, \"DataBase_Directory\");\n\t\tsql1.setLabelFor(dbDirText);\n\t\tsqlitePanel.add(sql1, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tsqlitePanel.add(sqlitedir, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 0.5, 0.0));\n\n\t\tJButton sqb1 = new JButton();\n\t\tResourceHelper.setText(sqb1, \"Browse\");\n\t\tsqb1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t// browse for new database dir\n\t\t\t\tString dbdir = OptionsPanel.chooseDir();\n\t\t\t\tif (dbdir == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// update text field - nothing else changes. DB change will take\n\t\t\t\t// effect\n\t\t\t\t// only on restart\n\t\t\t\tsqlitedir.setText(dbdir);\n\t\t\t}\n\t\t});\n\n\t\tsqlitePanel.add(sqb1, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH));\n\n\t\treturn sqlitePanel;\n\t}\n\n\t/**\n\t * get the generic jdbc options panel\n\t *\n\t * @return the jdbc options panel\n\t */\n\tprivate JPanel getJdbcPanel() {\n\t\tif (jdbcPanel == null) {\n\n\t\t\tJLabel enturlLabel = new JLabel();\n\t\t\tResourceHelper.setText(enturlLabel, \"enturl\");\n\t\t\tjdbcText = new JTextField();\n\t\t\tenturlLabel.setLabelFor(jdbcText);\n\t\t\tenturlLabel.setHorizontalTextPosition(SwingConstants.LEFT);\n\t\t\tenturlLabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\t\tjdbcPanel = new JPanel();\n\t\t\tjdbcPanel.setLayout(new GridBagLayout());\n\t\t\tjdbcPanel.setBorder(BorderFactory.createTitledBorder(null, Resource.getResourceString(\"jdbc\"),\n\t\t\t\t\tTitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));\n\t\t\tjdbcPanel.add(enturlLabel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\t\t\tjdbcPanel.add(jdbcText, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\t}\n\t\treturn jdbcPanel;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\t//\n\t\t// database type\n\t\t//\n\t\tString dbtype = Prefs.getPref(PrefName.DBTYPE);\n\t\tif (dbtype.equals(\"h2\")) {\n\t\t\th2Button.setSelected(true);\n\t\t} else if (dbtype.equals(\"sqlite\")) {\n\t\t\tsqliteButton.setSelected(true);\n\t\t} else {\n\t\t\tjdbcButton.setSelected(true);\n\t\t}\n\t\t// change to show the db panel that matches the db type\n\t\tdbTypeChange(dbtype);\n\n\t\tjdbcText.setText(Prefs.getPref(PrefName.JDBCURL));\n\t\th2dir.setText(Prefs.getPref(PrefName.H2DIR));\n\t\tsqlitedir.setText(Prefs.getPref(PrefName.SQLITEDIR));\n\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"DatabaseInformation\");\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/EmailOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPasswordField;\nimport javax.swing.JSpinner;\nimport javax.swing.JTextField;\nimport javax.swing.SpinnerDateModel;\n\nimport net.sf.borg.common.EncryptionHelper;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.PasswordHelper;\n\n/**\n * provides the UI for Email Options.\n */\nclass EmailOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = 795364188303457966L;\n\n\tprivate final JCheckBox emailbox = new JCheckBox();\n\tprivate final JCheckBox dailybox = new JCheckBox();\n\n\tprivate final JTextField emailtext = new JTextField();\n\tprivate final JTextField emailfrom = new JTextField();\n\n\tprivate JSpinner emailtimebox = new JSpinner();\n\n\tprivate final JPasswordField smpw = new JPasswordField();\n\n\tprivate final JTextField smtpport = new JTextField();\n\n\tprivate final JTextField smtptext = new JTextField();\n\n\tprivate final JCheckBox tlsbox = new JCheckBox();\n\n\tprivate final JTextField usertext = new JTextField();\n\n\t/**\n\t * Instantiates a new email options panel.\n\t */\n\tpublic EmailOptionsPanel() {\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\tResourceHelper.setText(emailbox, \"Enable_Email\");\n\t\tthis.add(emailbox, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(dailybox, \"Enable_Daily_Email\");\n\t\tthis.add(dailybox, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel1 = new JLabel();\n\t\tResourceHelper.setText(jLabel1, \"SMTP_Server\");\n\t\tthis.add(jLabel1, GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\t\tjLabel1.setLabelFor(smtptext);\n\n\t\tsmtptext.setColumns(30);\n\t\tthis.add(smtptext, GridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJLabel portLabel = new JLabel();\n\t\tResourceHelper.setText(portLabel, \"SMTP_Port\");\n\t\tthis.add(portLabel, GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n\t\tjLabel1.setLabelFor(smtpport);\n\n\t\tsmtpport.setColumns(30);\n\t\tthis.add(smtpport, GridBagConstraintsFactory.create(1, 3, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJLabel userlabel = new JLabel();\n\t\tResourceHelper.setText(userlabel, \"SMTP_user\");\n\t\tthis.add(userlabel, GridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n\t\tuserlabel.setLabelFor(usertext);\n\n\t\tthis.add(usertext, GridBagConstraintsFactory.create(1, 4, GridBagConstraints.BOTH));\n\n\t\tJLabel passlabel = new JLabel();\n\t\tResourceHelper.setText(passlabel, \"SMTP_password\");\n\t\tthis.add(passlabel, GridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\t\tpasslabel.setLabelFor(smpw);\n\n\t\tthis.add(smpw, GridBagConstraintsFactory.create(1, 5, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel2 = new JLabel();\n\t\tResourceHelper.setText(jLabel2, \"Your_Email_Address\");\n\t\tthis.add(jLabel2, GridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH));\n\t\tjLabel2.setLabelFor(emailtext);\n\n\t\temailtext.setColumns(30);\n\t\tthis.add(emailtext, GridBagConstraintsFactory.create(1, 6, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tthis.add(new JLabel(Resource.getResourceString(\"EmailFrom\")),\n\t\t\t\tGridBagConstraintsFactory.create(0, 7, GridBagConstraints.BOTH));\n\n\t\temailfrom.setColumns(30);\n\t\tthis.add(emailfrom, GridBagConstraintsFactory.create(1, 7, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJLabel remtimelabel = new JLabel();\n\t\tResourceHelper.setText(remtimelabel, \"reminder_time\");\n\t\tremtimelabel.setLabelFor(emailtimebox);\n\t\tthis.add(remtimelabel, GridBagConstraintsFactory.create(0, 8, GridBagConstraints.BOTH));\n\n\t\temailtimebox = new JSpinner(new SpinnerDateModel());\n\t\tJSpinner.DateEditor de = new JSpinner.DateEditor(emailtimebox, \"HH:mm\");\n\t\temailtimebox.setEditor(de);\n\t\tthis.add(emailtimebox, GridBagConstraintsFactory.create(1, 8, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\ttlsbox.setText(Resource.getResourceString(\"enable_tls\"));\n\t\tthis.add(tlsbox, GridBagConstraintsFactory.create(0, 9, GridBagConstraints.BOTH));\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\n\t\tOptionsPanel.setBooleanPref(emailbox, PrefName.EMAILENABLED);\n\t\tOptionsPanel.setBooleanPref(dailybox, PrefName.DAILYEMAILENABLED);\n\t\tOptionsPanel.setBooleanPref(tlsbox, PrefName.ENABLETLS);\n\n\t\t// only save email options if email is enabled\n\t\tif (emailbox.isSelected()) {\n\t\t\tPrefs.putPref(PrefName.EMAILSERVER, smtptext.getText());\n\t\t\tPrefs.putPref(PrefName.EMAILPORT, smtpport.getText());\n\t\t\tPrefs.putPref(PrefName.EMAILADDR, emailtext.getText());\n\t\t\tPrefs.putPref(PrefName.EMAILFROM, emailfrom.getText());\n\t\t\tPrefs.putPref(PrefName.EMAILUSER, usertext.getText());\n\n\t\t\ttry {\n\t\t\t\tString pw = new String(smpw.getPassword());\n\t\t\t\tif (pw != null && !pw.isEmpty()) {\n\n\t\t\t\t\tString kpw = PasswordHelper.getReference().getEncryptionKeyWithoutTimeout(\"Encrypt EMail Password\");\n\t\t\t\t\tif (kpw != null) {\n\t\t\t\t\t\tEncryptionHelper helper = new EncryptionHelper(kpw);\n\t\t\t\t\t\tPrefs.putPref(PrefName.EMAILPASS, helper.encrypt(pw));\n\t\t\t\t\t\tsmpw.setText(\"\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t}\n\n\t\tDate d = (Date) emailtimebox.getValue();\n\t\tCalendar cal = new GregorianCalendar();\n\t\tcal.setTime(d);\n\t\tint hour = cal.get(Calendar.HOUR_OF_DAY);\n\t\tint min = cal.get(Calendar.MINUTE);\n\t\tPrefs.putPref(PrefName.EMAILTIME, Integer.valueOf(hour * 60 + min));\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\n\t\t// email time to day\n\t\tint emmins = Prefs.getIntPref(PrefName.EMAILTIME);\n\t\tCalendar cal = new GregorianCalendar(1980, 1, 1, 0, 0, 0);\n\t\tcal.add(Calendar.MINUTE, emmins);\n\t\temailtimebox.setValue(cal.getTime());\n\n\t\tOptionsPanel.setCheckBox(emailbox, PrefName.EMAILENABLED);\n\t\tOptionsPanel.setCheckBox(dailybox, PrefName.DAILYEMAILENABLED);\n\t\tOptionsPanel.setCheckBox(tlsbox, PrefName.ENABLETLS);\n\n\t\t// email server and address\n\t\tsmtptext.setText(Prefs.getPref(PrefName.EMAILSERVER));\n\t\tsmtpport.setText(Prefs.getPref(PrefName.EMAILPORT));\n\t\temailtext.setText(Prefs.getPref(PrefName.EMAILADDR));\n\t\temailfrom.setText(Prefs.getPref(PrefName.EMAILFROM));\n\t\tusertext.setText(Prefs.getPref(PrefName.EMAILUSER));\n\n\t\tsmpw.setText(\"\");\n\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"EmailParameters\");\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/EncryptionOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.io.File;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JPasswordField;\nimport javax.swing.JSpinner;\nimport javax.swing.JTextArea;\nimport javax.swing.JTextField;\nimport javax.swing.SpinnerNumberModel;\n\nimport net.sf.borg.common.EncryptionHelper;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\n/**\n * Provides the UI for editing Encryption options\n */\npublic class EncryptionOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = 3485306815964774388L;\n\n\tprivate final JTextField keyStoreText = new JTextField();\n\n\tprivate JSpinner passwordTimeSpinner = null;\n\n\t/**\n\t * Instantiates a new encryption options panel.\n\t */\n\tpublic EncryptionOptionsPanel() {\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\tJPanel ksPanel = new JPanel();\n\t\tksPanel.setLayout(new GridBagLayout());\n\n\t\tksPanel\n\t\t\t\t.add(new JLabel(Resource.getResourceString(\"KeyStore\") + \": \"),\n\t\t\t\t\t\tGridBagConstraintsFactory.create(0, 0,\n\t\t\t\t\t\t\t\tGridBagConstraints.NONE));\n\n\t\tkeyStoreText.setEditable(false);\n\t\tksPanel.add(keyStoreText, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJButton bb = new JButton();\n\t\tResourceHelper.setText(bb, \"Browse\");\n\t\tbb.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tJFileChooser chooser = new JFileChooser();\n\n\t\t\t\tchooser.setCurrentDirectory(new File(System\n\t\t\t\t\t\t.getProperty(\"user.home\")));\n\t\t\t\tchooser.setDialogTitle(Resource\n\t\t\t\t\t\t.getResourceString(\"SelectKeyStore\"));\n\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\treturn;\n\n\t\t\t\tFile file = chooser.getSelectedFile();\n\n\t\t\t\tif (!file.canRead()) {\n\t\t\t\t\t// create keystore\n\t\t\t\t\tJTextArea ta = new JTextArea();\n\t\t\t\t\tta.setText(Resource.getResourceString(\"create_key_store\"));\n\t\t\t\t\tta.setEditable(false);\n\t\t\t\t\tta.setRows(2);\n\t\t\t\t\tJPasswordField jpf = new JPasswordField();\n\t\t\t\t\tJPasswordField jpf2 = new JPasswordField();\n\t\t\t\t\tint result = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\tnew Object[] { ta, jpf, jpf2 }, Resource\n\t\t\t\t\t\t\t\t\t.getResourceString(\"Password\"),\n\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\tif (result == JOptionPane.CANCEL_OPTION)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\tif (!new String(jpf.getPassword()).equals(new String(jpf2\n\t\t\t\t\t\t\t.getPassword()))) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"PasswordsDoNotMatch\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif( new String(jpf.getPassword()).trim().isEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"PasswordWhitespace\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tEncryptionHelper.createDefaultStore(file.getAbsolutePath(),\n\t\t\t\t\t\t\t\tnew String(jpf.getPassword()));\t\t\t\t\t\t\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tkeyStoreText.setText(file.getAbsolutePath());\n\t\t\t}\n\t\t});\n\t\tksPanel.add(bb, GridBagConstraintsFactory.create(2, 0,\n\t\t\t\tGridBagConstraints.NONE));\n\n\t\tGridBagConstraints gbc1 = GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0);\n\t\tgbc1.gridwidth = 2;\n\t\tthis.add(ksPanel, gbc1);\n\n\t\tthis\n\t\t\t\t.add(new JLabel(Resource.getResourceString(\"pw_time\")),\n\t\t\t\t\t\tGridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tpasswordTimeSpinner = new JSpinner(new SpinnerNumberModel(0, 0,\n\t\t\t\t60 * 60 * 24 * 365, 1));\n\n\t\tthis.add(passwordTimeSpinner, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH));\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tPrefs.putPref(PrefName.KEYSTORE, keyStoreText.getText());\n\t\tPrefs.putPref(PrefName.PASSWORD_TTL, passwordTimeSpinner.getValue());\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tkeyStoreText.setText(Prefs.getPref(PrefName.KEYSTORE));\n\t\tpasswordTimeSpinner.setValue(Integer.valueOf(Prefs\n\t\t\t\t.getIntPref(PrefName.PASSWORD_TTL)));\n\n\t}\n\t\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"Encryption\");\n\t}\n\t\n\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/FontOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.Font;\nimport java.awt.Frame;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\n\nimport javax.swing.JButton;\nimport javax.swing.JTextField;\nimport javax.swing.SwingUtilities;\nimport javax.swing.WindowConstants;\nimport javax.swing.border.BevelBorder;\nimport javax.swing.border.SoftBevelBorder;\n\nimport org.drjekyll.fontchooser.FontDialog;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.UIControl;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\n/**\n * Provides the UI for editing Font options\n */\npublic class FontOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = -6568983838009839140L;\n\t\n\tJTextField monthFontText = new JTextField();\n\tJTextField dayFontText = new JTextField();\n\tJTextField weekFontText = new JTextField();\n\tJTextField yearFontText = new JTextField();\n\tJTextField printFontText = new JTextField();\n\tJTextField defaultFontText = new JTextField();\n\tJTextField trayFontText = new JTextField();\n\n\t/**\n\t * Instantiates a new font options panel.\n\t */\n\tpublic FontOptionsPanel() {\n\t\t\n\t\tmonthFontText.setEditable(false);\n\t\tdayFontText.setEditable(false);\n\t\tweekFontText.setEditable(false);\n\t\tyearFontText.setEditable(false);\n\t\tprintFontText.setEditable(false);\n\t\tdefaultFontText.setEditable(false);\n\t\ttrayFontText.setEditable(false);\n\t\t\n\t\tthis.setLayout(new GridBagLayout());\n\t\t\n\t\tJButton defFontButton = new JButton();\n\t\tResourceHelper.setText(defFontButton, \"set_def_font\");\n\t\tdefFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tdefFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(defaultFontText);\n\t\t\t}\n\t\t});\n\t\n\t\tJButton apptFontButton = new JButton();\n\t\tResourceHelper.setText(apptFontButton, \"set_appt_font\");\n\t\tapptFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tapptFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(monthFontText);\n\t\t\t}\n\t\t});\n\n\t\tJButton dayFontButton = new JButton();\n\t\tResourceHelper.setText(dayFontButton, \"dview_font\");\n\t\tdayFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tdayFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(dayFontText);\n\t\t\t}\n\t\t});\n\n\t\tJButton weekFontButton = new JButton();\n\t\tResourceHelper.setText(weekFontButton, \"wview_font\");\n\t\tweekFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tweekFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(weekFontText);\n\t\t\t}\n\t\t});\n\n\t\tJButton printFontButton = new JButton();\n\t\tResourceHelper.setText(printFontButton, \"mview_font\");\n\t\tprintFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tprintFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(printFontText);\n\t\t\t}\n\t\t});\n\t\t\n\t\tJButton yearFontButton = new JButton();\n\t\tResourceHelper.setText(yearFontButton, \"yview_font\");\n\t\tyearFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\tyearFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(yearFontText);\n\t\t\t}\n\t\t});\n\t\t\n\t\tJButton trayFontButton = new JButton();\n\t\tResourceHelper.setText(trayFontButton, \"tray_font\");\n\t\ttrayFontButton.setBorder(new SoftBevelBorder(BevelBorder.RAISED));\n\t\ttrayFontButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfontActionPerformed(trayFontText);\n\t\t\t}\n\t\t});\n\t\t\n\t\tthis.add(defFontButton, GridBagConstraintsFactory.create(0,0, GridBagConstraints.BOTH));\n\t\tthis.add(apptFontButton, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\t\tthis.add(dayFontButton, GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\t\tthis.add(weekFontButton, GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n\t\tthis.add(yearFontButton, GridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n\t\tthis.add(printFontButton, GridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\t\tthis.add(trayFontButton, GridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH));\n\t\t\n\t\tthis.add(defaultFontText, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(monthFontText, GridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(weekFontText, GridBagConstraintsFactory.create(1, 3, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(dayFontText, GridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(yearFontText, GridBagConstraintsFactory.create(1, 4, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(printFontText, GridBagConstraintsFactory.create(1, 5, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tthis.add(trayFontText, GridBagConstraintsFactory.create(1, 6, GridBagConstraints.BOTH, 1.0, 0.0));\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\t\n\t\tString origDefaultFont = Prefs.getPref(PrefName.DEFFONT);\n\t\t\n\t\tPrefs.putPref(PrefName.APPTFONT, monthFontText.getText());\n\t\tPrefs.putPref(PrefName.DEFFONT, defaultFontText.getText());\n\t\tPrefs.putPref(PrefName.WEEKVIEWFONT, weekFontText.getText());\n\t\tPrefs.putPref(PrefName.YEARVIEWFONT, yearFontText.getText());\n\t\tPrefs.putPref(PrefName.DAYVIEWFONT, dayFontText.getText());\n\t\tPrefs.putPref(PrefName.PRINTFONT, printFontText.getText());\n\t\tPrefs.putPref(PrefName.TRAYFONT, trayFontText.getText());\n\t\t\n\t\t// if the default font is changing then try to update the entire UI\n\t\t// will not likely be pretty\n\t\tif (!origDefaultFont.equals(defaultFontText.getText())) {\n\t\t\tFont f = Font.decode(defaultFontText.getText());\n\t\t\tUIControl.setDefaultFont(f);\n\t\t\tSwingUtilities.updateComponentTreeUI(this);\n\t\t}\n\n\t}\n\t\n\t static private String fontString(Font font) {\n\t        String fs = font.getName();\n\t        /*if (!font.isPlain()) {\n\t            fs += \"-\";\n\t            if (font.isBold()) {\n\t                fs += \"BOLD\";\n\t            }\n\t            if (font.isItalic()) {\n\t                fs += \"ITALIC\";\n\t            }\n\t        }*/\n\t        fs += \"-\" + font.getSize();\n\t        return (fs);\n\t    }\n\n\n\t/**\n\t * bring up a font chooser UI and let the user change a font\n\t * \n\t * @param fontname\n\t *            the preference name associated with the font\n\t */\n\tprivate static void fontActionPerformed(JTextField fontText) {\n\n\t\t// get font from pref name\n\t\tFont pf = Font.decode(fontText.getText());\n\n\t\t// choose a new font\n\t\tFontDialog dialog = new FontDialog((Frame) null, Resource.getResourceString(\"font_chooser\"), true);\n        dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n        dialog.setSelectedFont(pf);\n        dialog.setVisible(true);\n        if( dialog.isCancelSelected())\n        \treturn;\n       \n\n\t\tFont f = dialog.getSelectedFont();\n\t\tif (f == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// get the font name and store\n\t\tString s = fontString(f);\n\t\tfontText.setText(s);\n\t\t\n\t\t\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tmonthFontText.setText(Prefs.getPref(PrefName.APPTFONT));\n\t\tdefaultFontText.setText(Prefs.getPref(PrefName.DEFFONT));\n\t\tweekFontText.setText(Prefs.getPref(PrefName.WEEKVIEWFONT));\n\t\tyearFontText.setText(Prefs.getPref(PrefName.YEARVIEWFONT));\n\t\tdayFontText.setText(Prefs.getPref(PrefName.DAYVIEWFONT));\n\t\tprintFontText.setText(Prefs.getPref(PrefName.PRINTFONT));\n\t\ttrayFontText.setText(Prefs.getPref(PrefName.TRAYFONT));\n\t}\n\t\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"fonts\");\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/GoogleOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSpinner;\nimport javax.swing.JTextField;\nimport javax.swing.SpinnerNumberModel;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.sync.google.GCal;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\n/**\n * Provides the UI for editing Miscellaneous options\n */\n\npublic class GoogleOptionsPanel extends OptionsPanel {\n\n\n\n    private static final long serialVersionUID = 2246952528811147049L;\n    private final JCheckBox enable_sync_box = new JCheckBox();\n\n    private final JTextField cal_box = new JTextField();\n    private final JTextField task_list_box = new JTextField();\n    private final JTextField credentials_file_box = new JTextField();\n    private final JTextField token_dir_box = new JTextField();\n    private final JTextField sub_box = new JTextField();\n    private final JTextField todo_cal_box = new JTextField();\n    private final JTextField gfile_box = new JTextField();\n\n\n    private final JSpinner exportyears = new JSpinner(new SpinnerNumberModel(2, 1,\n\t\t\t100, 1));\n\n    /**\n     * Instantiates a new miscellaneous options panel.\n     */\n    public GoogleOptionsPanel() {\n\n        this.setLayout(new GridBagLayout());\n\n        enable_sync_box.setText(\"Enable Google Sync\");\n\n        this.add(enable_sync_box,\n                GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n        \n        this.add(new JLabel(Resource.getResourceString(\"years_to_export\")),\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n        GridBagConstraints spin_gbc = GridBagConstraintsFactory.create(1, 1);\n        spin_gbc.anchor = GridBagConstraints.WEST;\n\t\tthis.add(exportyears, spin_gbc );\n\n        this.add(new JLabel(\"Calendar:\"),\n                GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n        this.add(cal_box,\n                GridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH));\n\n        this.add(new JLabel(\"Task List:\"),\n                GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n        this.add(task_list_box,\n                GridBagConstraintsFactory.create(1, 3, GridBagConstraints.BOTH));\n\n        this.add(new JLabel(\"Subscribed:\"),\n                GridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n        this.add(sub_box,\n                GridBagConstraintsFactory.create(1, 4, GridBagConstraints.BOTH));\n        \n        this.add(new JLabel(\"Optional Todo Calendar:\"),\n                GridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n        this.add(todo_cal_box,\n                GridBagConstraintsFactory.create(1, 5, GridBagConstraints.BOTH));\n\n        JPanel filep = new JPanel();\n        filep.setLayout(new GridBagLayout());\n\n        filep.add(new JLabel(\"Credentials File:\"),\n                GridBagConstraintsFactory.create(0, 0, GridBagConstraints.NONE));\n\n        filep.add(credentials_file_box, GridBagConstraintsFactory.create(1, 0,\n                GridBagConstraints.BOTH, 1.0, 0.0));\n\n        JButton bb = new JButton();\n        ResourceHelper.setText(bb, \"Browse\");\n        bb.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                String f = OptionsPanel.chooseFile();\n                if (f == null) {\n                    return;\n                }\n\n                credentials_file_box.setText(f);\n            }\n        });\n        filep.add(bb,\n                GridBagConstraintsFactory.create(2, 0, GridBagConstraints.NONE));\n\n        filep.add(new JLabel(\"Tokens Dir:\"),\n                GridBagConstraintsFactory.create(0, 1, GridBagConstraints.NONE));\n\n        filep.add(token_dir_box, GridBagConstraintsFactory.create(1, 1,\n                GridBagConstraints.BOTH, 1.0, 0.0));\n\n        JButton tb = new JButton();\n        ResourceHelper.setText(tb, \"Browse\");\n        tb.addActionListener(new java.awt.event.ActionListener() {\n            @Override\n            public void actionPerformed(java.awt.event.ActionEvent evt) {\n                String f = OptionsPanel.chooseDir();\n                if (f == null) {\n                    return;\n                }\n\n                token_dir_box.setText(f);\n            }\n        });\n        filep.add(tb,\n                GridBagConstraintsFactory.create(2, 1, GridBagConstraints.NONE));\n\n        GridBagConstraints gbc1 = GridBagConstraintsFactory.create(0, 6,\n                GridBagConstraints.BOTH, 1.0, 0.0);\n        gbc1.gridwidth = 2;\n        this.add(filep, gbc1);\n        \n        this.add(new JLabel(\"Google Drive File Path:\"),\n                GridBagConstraintsFactory.create(0, 7, GridBagConstraints.BOTH));\n        this.add(gfile_box,\n                GridBagConstraintsFactory.create(1, 7, GridBagConstraints.BOTH));\n\n\n\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n     */\n    @Override\n    public void applyChanges() {\n\n\t\tPrefs.putPref(PrefName.GCAL_EXPORTYEARS, exportyears.getValue());\n\n        OptionsPanel.setBooleanPref(enable_sync_box, PrefName.GOOGLE_SYNC);\n        Prefs.putPref(PrefName.GCAL_CAL_ID, cal_box.getText());\n        Prefs.putPref(PrefName.GCAL_TASKLIST_ID, task_list_box.getText());\n        Prefs.putPref(PrefName.GOOGLE_CRED_FILE, credentials_file_box.getText());\n        Prefs.putPref(PrefName.GOOGLE_TOKEN_DIR, token_dir_box.getText());\n        Prefs.putPref(PrefName.GOOGLE_SUBSCRIBED, sub_box.getText());\n        Prefs.putPref(PrefName.GCAL_TODO_CAL_ID, todo_cal_box.getText());\n        Prefs.putPref(PrefName.GOOGLE_DB_FILE_PATH, gfile_box.getText());\n\n        GCal.getReference().resetGoogleIds();\n\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n     */\n    @Override\n    public void loadOptions() {\n        OptionsPanel.setCheckBox(enable_sync_box, PrefName.GOOGLE_SYNC);\n        cal_box.setText(Prefs.getPref(PrefName.GCAL_CAL_ID));\n        task_list_box.setText(Prefs.getPref(PrefName.GCAL_TASKLIST_ID));\n        credentials_file_box.setText(Prefs.getPref(PrefName.GOOGLE_CRED_FILE));\n        token_dir_box.setText(Prefs.getPref(PrefName.GOOGLE_TOKEN_DIR));\n\t\texportyears.setValue(Prefs.getIntPref(PrefName.GCAL_EXPORTYEARS));\n        sub_box.setText(Prefs.getPref(PrefName.GOOGLE_SUBSCRIBED));\n        todo_cal_box.setText(Prefs.getPref(PrefName.GCAL_TODO_CAL_ID));\n        gfile_box.setText(Prefs.getPref(PrefName.GOOGLE_DB_FILE_PATH));\n\n\n    }\n\n    @Override\n    public String getPanelName() {\n        return \"Google\";\n    }\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/IcalOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\n\nimport javax.swing.JCheckBox;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JSpinner;\nimport javax.swing.SpinnerNumberModel;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\npublic class IcalOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = 795364188303457966L;\n\n\tprivate final JSpinner exportyears = new JSpinner(new SpinnerNumberModel(2, 1, 100, 1));\n\n\tprivate final JCheckBox todoBox = new JCheckBox();\n\n\t\n\n\tpublic IcalOptionsPanel() {\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\t\n\n\t\tthis.add(new JLabel(Resource.getResourceString(\"years_to_export\")),\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\t\tthis.add(exportyears, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH));\n\n\t\ttodoBox.setText(Resource.getResourceString(\"ical_export_todos\"));\n\t\tthis.add(todoBox, GridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\t\n\n\t}\n\n\t@Override\n\tpublic void applyChanges() {\n\n\t\tPrefs.putPref(PrefName.ICAL_EXPORTYEARS, exportyears.getValue());\n\n\t\tOptionsPanel.setBooleanPref(todoBox, PrefName.ICAL_EXPORT_TODO);\n\n\t\t\n\t}\n\n\t@Override\n\tpublic void loadOptions() {\n\n\t\texportyears.setValue(Prefs.getIntPref(PrefName.ICAL_EXPORTYEARS));\n\n\t\ttodoBox.setSelected(Prefs.getBoolPref(PrefName.ICAL_EXPORT_TODO));\n\n\t\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"ical_options\");\n\t}\n\n\tstatic public void main(String[] args) {\n\t\tJFrame jf = new JFrame();\n\t\tjf.setContentPane(new IcalOptionsPanel());\n\t\tjf.setVisible(true);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/MiscellaneousOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Provides the UI for editing Miscellaneous options\n */\n\npublic class MiscellaneousOptionsPanel extends OptionsPanel {\n\t\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\tstatic private final Logger auditLog = Logger.getLogger(\"net.sf.borg.audit\");\n\n\n\tpublic enum SHUTDOWN_ACTION {\n\t\tPROMPT, BACKUP, NONE\n\t}\n\n\tprivate static final long serialVersionUID = 2246952528811147049L;\n\tprivate final JTextField backupDir = new JTextField();\n\tprivate final JCheckBox colorprint = new JCheckBox();\n\n\tprivate final JTextField socketPort = new JTextField();\n\tprivate final JCheckBox splashbox = new JCheckBox();\n\tprivate final JCheckBox stackbox = new JCheckBox();\n\n\tprivate final JCheckBox useSysTray = new JCheckBox();\n\tprivate final JCheckBox startToSysTray = new JCheckBox();\n\tprivate final JCheckBox dateInSysTray = new JCheckBox();\n\n\tprivate final JCheckBox verboseLogging = new JCheckBox();\n\tprivate final JCheckBox auditLogging = new JCheckBox();\n\n\tprivate final JComboBox<String> shutdownAction = new JComboBox<String>();\n\n\t/**\n\t * Instantiates a new miscellaneous options panel.\n\t */\n\tpublic MiscellaneousOptionsPanel() {\n\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\tResourceHelper.setText(splashbox, \"splash\");\n\t\tthis.add(splashbox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tResourceHelper.setText(stackbox, \"stackonerr\");\n\t\tthis.add(stackbox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n\t\tJLabel sportlabel = new JLabel();\n\t\tResourceHelper.setText(sportlabel, \"socket_port\");\n\t\tthis.add(sportlabel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\n\t\tthis.add(socketPort,\n\t\t\t\tGridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH));\n\n\t\tuseSysTray.setText(Resource.getResourceString(\"enable_systray\"));\n\t\tthis.add(useSysTray,\n\t\t\t\tGridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n\n\t\tstartToSysTray.setText(Resource.getResourceString(\"StartToSysTray\"));\n\t\tthis.add(startToSysTray,\n\t\t\t\tGridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n\n\t\tdateInSysTray.setText(Resource\n\t\t\t\t.getResourceString(\"show_date_in_systray\"));\n\t\tthis.add(dateInSysTray,\n\t\t\t\tGridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\n\t\tJPanel backp = new JPanel();\n\t\tbackp.setLayout(new GridBagLayout());\n\n\t\tbackp.add(new JLabel(Resource.getResourceString(\"backup_dir\") + \": \"),\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.NONE));\n\n\t\tbackp.add(backupDir, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJButton bb = new JButton();\n\t\tResourceHelper.setText(bb, \"Browse\");\n\t\tbb.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tString dbdir = OptionsPanel.chooseDir();\n\t\t\t\tif (dbdir == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbackupDir.setText(dbdir);\n\t\t\t}\n\t\t});\n\t\tbackp.add(bb,\n\t\t\t\tGridBagConstraintsFactory.create(2, 0, GridBagConstraints.NONE));\n\n\t\tbackp.add(new JLabel(Resource.getResourceString(\"on_shutdown\")),\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n\t\tshutdownAction.addItem(Resource.getResourceString(\"prompt_for_backup\"));\n\t\tshutdownAction.addItem(Resource.getResourceString(\"exit_no_backup\"));\n\t\tshutdownAction.addItem(Resource.getResourceString(\"write_backup_file\"));\n\n\t\tbackp.add(shutdownAction, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tGridBagConstraints gbc1 = GridBagConstraintsFactory.create(0, 6,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0);\n\t\tgbc1.gridwidth = 2;\n\t\tthis.add(backp, gbc1);\n\n\t\t\n\t\tResourceHelper.setText(colorprint, \"Print_In_Color?\");\n\t\tthis.add(colorprint,\n\t\t\t\tGridBagConstraintsFactory.create(0, 8, GridBagConstraints.BOTH));\n\n\t\n\t\tverboseLogging.setText(Resource.getResourceString(\"verbose_logging\"));\n\t\tgbc1 = GridBagConstraintsFactory.create(0, 10, GridBagConstraints.BOTH);\n\t\tgbc1.gridwidth = 2;\n\t\tthis.add(verboseLogging, gbc1);\n\n\t\tauditLogging.setText(Resource.getResourceString(\"audit_logging\"));\n\t\tgbc1 = GridBagConstraintsFactory.create(0, 11, GridBagConstraints.BOTH);\n\t\tgbc1.gridwidth = 2;\n\t\tthis.add(auditLogging, gbc1);\n\n\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\t\n\t\tif (verboseLogging.isSelected())\n\t\t\tlog.setLevel(Level.ALL);\n\t\telse\n\t\t\tlog.setLevel(Level.INFO);\n\n\t\tif (auditLogging.isSelected())\n\t\t\tauditLog.setLevel(Level.INFO);\n\t\telse\n\t\t\tauditLog.setLevel(Level.SEVERE);\n\n\t\t\n\t\tOptionsPanel.setBooleanPref(colorprint, PrefName.COLORPRINT);\n\n\t\tOptionsPanel.setBooleanPref(splashbox, PrefName.SPLASH);\n\t\tOptionsPanel.setBooleanPref(stackbox, PrefName.STACKTRACE);\n\n\t\tOptionsPanel.setBooleanPref(useSysTray, PrefName.USESYSTRAY);\n\t\tOptionsPanel.setBooleanPref(startToSysTray, PrefName.BACKGSTART);\n\t\tOptionsPanel.setBooleanPref(dateInSysTray, PrefName.SYSTRAYDATE);\n\t\tOptionsPanel.setBooleanPref(verboseLogging, PrefName.DEBUG);\n\t\tOptionsPanel.setBooleanPref(auditLogging, PrefName.AUDITLOG);\n\n\t\tPrefs.putPref(PrefName.BACKUPDIR, backupDir.getText());\n\n\t\tif (shutdownAction.getSelectedIndex() == 0)\n\t\t\tPrefs.putPref(PrefName.SHUTDOWN_ACTION, SHUTDOWN_ACTION.PROMPT.toString());\n\t\telse if (shutdownAction.getSelectedIndex() == 1)\n\t\t\tPrefs.putPref(PrefName.SHUTDOWN_ACTION, SHUTDOWN_ACTION.NONE.toString());\n\t\telse if (shutdownAction.getSelectedIndex() == 2)\n\t\t\tPrefs.putPref(PrefName.SHUTDOWN_ACTION, SHUTDOWN_ACTION.BACKUP.toString());\n\t\n\t\t// validate that socket is a number\n\t\ttry {\n\t\t\tint socket = Integer.parseInt(socketPort.getText());\n\t\t\tPrefs.putPref(PrefName.SOCKETPORT, Integer.valueOf(socket));\n\t\t} catch (NumberFormatException e) {\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"socket_warn\"));\n\t\t\tsocketPort.setText(\"-1\");\n\t\t\tPrefs.putPref(PrefName.SOCKETPORT, Integer.valueOf(-1));\n\t\t}\n\t\t\n\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tbackupDir.setText(Prefs.getPref(PrefName.BACKUPDIR));\n\t\tOptionsPanel.setCheckBox(colorprint, PrefName.COLORPRINT);\n\n\t\tOptionsPanel.setCheckBox(splashbox, PrefName.SPLASH);\n\t\tOptionsPanel.setCheckBox(stackbox, PrefName.STACKTRACE);\n\n\t\tOptionsPanel.setCheckBox(useSysTray, PrefName.USESYSTRAY);\n\t\tOptionsPanel.setCheckBox(startToSysTray, PrefName.BACKGSTART);\n\t\tOptionsPanel.setCheckBox(dateInSysTray, PrefName.SYSTRAYDATE);\n\t\tOptionsPanel.setCheckBox(verboseLogging, PrefName.DEBUG);\n\t\tOptionsPanel.setCheckBox(auditLogging, PrefName.AUDITLOG);\n\n\t\tString shutdown_action = Prefs.getPref(PrefName.SHUTDOWN_ACTION);\n\t\tif (shutdown_action.isEmpty()\n\t\t\t\t|| SHUTDOWN_ACTION.PROMPT.toString().equals(shutdown_action))\n\t\t\tshutdownAction.setSelectedIndex(0);\n\t\telse if (SHUTDOWN_ACTION.NONE.toString().equals(shutdown_action))\n\t\t\tshutdownAction.setSelectedIndex(1);\n\t\telse if (SHUTDOWN_ACTION.BACKUP.toString().equals(shutdown_action))\n\t\t\tshutdownAction.setSelectedIndex(2);\n\t\t\n\n\t\tint socket = Prefs.getIntPref(PrefName.SOCKETPORT);\n\t\tsocketPort.setText(Integer.toString(socket));\n\n\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"misc\");\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/OptionsView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JFileChooser;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.WindowConstants;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.View;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * UI for editing BORG options\n */\npublic class OptionsView extends View {\n\n    private static final long serialVersionUID = 1L;\n    /**\n     * size of the option window.\n     */\n    static private final PrefName OPTVIEWSIZE = new PrefName(\"optviewsize\",\n            \"-1,-1,-1,-1,N\");\n    private static OptionsView singleton = null;\n    private JButton applyButton;\n    private JButton dismissButton;\n    private final JTabbedPane jTabbedPane1;\n    private JPanel topPanel = null;\n    private final Collection<OptionsPanel> panels = new ArrayList<OptionsPanel>();\n\n    /**\n     * constructor\n     *\n     * @param dbonly if true, restrict changes to selecting the db only\n     */\n    private OptionsView(boolean dbonly) {\n        super();\n\n        jTabbedPane1 = new JTabbedPane();\n\n        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n        this.setTitle(Resource.getResourceString(\"Options\"));\n        addWindowListener(new java.awt.event.WindowAdapter() {\n            @Override\n            public void windowClosing(java.awt.event.WindowEvent evt) {\n                destroy();\n            }\n        });\n\n        topPanel = new JPanel();\n        topPanel.setLayout(new GridBagLayout());\n\n        topPanel.add(jTabbedPane1, GridBagConstraintsFactory.create(0, 0,\n                GridBagConstraints.BOTH, 1.0, 1.0));\n\n        if (dbonly) {\n            // setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);\n        } else {\n\n            dismissButton = new JButton();\n            applyButton = new JButton();\n\n            JPanel applyDismissPanel = new JPanel();\n\n            applyButton.setIcon(IconHelper.getIcon(\n                    \"/resource/Save16.gif\"));\n            ResourceHelper.setText(applyButton, \"apply\");\n            applyButton.addActionListener(new java.awt.event.ActionListener() {\n                @Override\n                public void actionPerformed(java.awt.event.ActionEvent evt) {\n                    applyChanges();\n                }\n            });\n            applyDismissPanel.add(applyButton, null);\n\n            dismissButton.setIcon(IconHelper.getIcon(\n                    \"/resource/Stop16.gif\"));\n            ResourceHelper.setText(dismissButton, \"Dismiss\");\n            dismissButton\n                    .addActionListener(new java.awt.event.ActionListener() {\n                        @Override\n                        public void actionPerformed(\n                                java.awt.event.ActionEvent evt) {\n                            destroy();\n                        }\n                    });\n            setDismissButton(dismissButton);\n            applyDismissPanel.add(dismissButton, null);\n\n            topPanel.add(applyDismissPanel, GridBagConstraintsFactory.create(0,\n                    1, GridBagConstraints.BOTH));\n\n            addPanel(new AppearanceOptionsPanel());\n            addPanel(new FontOptionsPanel());\n            addPanel(new EmailOptionsPanel());\n            addPanel(new PopupOptionsPanel());\n            addPanel(new MiscellaneousOptionsPanel());\n            addPanel(new ColorOptionsPanel());\n            addPanel(new TaskOptionsPanel());\n            addPanel(new TodoOptionsPanel());\n            addPanel(new EncryptionOptionsPanel());\n            addPanel(new StartupViewsOptionsPanel());\n        }\n\n        addPanel(new DatabaseOptionsPanel());\n\n        JScrollPane sp = new JScrollPane();\n        sp.setViewportView(topPanel);\n        this.setContentPane(sp);\n        this.setSize(629, 493);\n\n        pack();\n\n        // automatically maintain the size and position of this view in\n        // a preference\n        if (!dbonly)\n            manageMySize(OPTVIEWSIZE);\n    }\n\n    /**\n     * open the options window but with a restricted view that only allows the\n     * db to be set\n     */\n    public static void dbSelectOnly() {\n        singleton = new OptionsView(true);\n        singleton.setVisible(true);\n    }\n\n    /**\n     * get the options view singleton\n     *\n     * @return the singleton\n     */\n    public static OptionsView getReference() {\n        if (singleton == null) {\n            singleton = new OptionsView(false);\n        } else if (!singleton.isShowing()) {\n            // reload options to reset the tabs\n            for (int t = 0; t < singleton.jTabbedPane1.getTabCount(); t++) {\n                OptionsPanel panel = (OptionsPanel) singleton.jTabbedPane1.getComponentAt(t);\n                panel.loadOptions();\n            }\n        }\n\n        return (singleton);\n    }\n\n    /**\n     * save all preferences to the preference store based on the current UI\n     * values\n     */\n    private void applyChanges() {\n\n        for (OptionsPanel panel : panels)\n            panel.applyChanges();\n\n        // notify all parts of borg that have registered to know about\n        // options changes\n        Prefs.notifyListeners();\n\n        if (TrayIconProxy.hasTrayIcon())\n            TrayIconProxy.getReference().updateImage();\n\n    }\n\n    /**\n     * destroy this view\n     */\n    @Override\n    public void destroy() {\n        this.dispose();\n    }\n\n    @Override\n    public void refresh() {\n        // empty\n    }\n\n    @Override\n    public void update(ChangeEvent event) {\n        refresh();\n    }\n\n    /**\n     * add an options panel to the options view\n     *\n     * @param panel - the panel\n     */\n    public void addPanel(OptionsPanel panel) {\n        panel.loadOptions();\n        jTabbedPane1.addTab(panel.getPanelName(), panel);\n        // jTabbedPane1.add(panel.getPanelName(), panel);\n        panels.add(panel);\n    }\n\n    /**\n     * abstract base class for tabs in the options view\n     */\n    static public abstract class OptionsPanel extends JPanel {\n        private static final long serialVersionUID = -4942616624428977307L;\n\n        /**\n         * set a boolean preference from a checkbox\n         *\n         * @param box the checkbox\n         * @param pn  the preference name\n         */\n        static public void setBooleanPref(JCheckBox box, PrefName pn) {\n            if (box.isSelected()) {\n                Prefs.putPref(pn, \"true\");\n            } else {\n                Prefs.putPref(pn, \"false\");\n            }\n        }\n\n        /**\n         * set a check box from a boolean preference\n         *\n         * @param box the checkbox\n         * @param pn  the preference name\n         */\n        static public void setCheckBox(JCheckBox box, PrefName pn) {\n            String val = Prefs.getPref(pn);\n            box.setSelected(val.equals(\"true\"));\n        }\n\n        /**\n         * Prompt the user to choose a folder\n         *\n         * @return the folder path or null\n         */\n        static String chooseDir() {\n\n            String path = null;\n            while (true) {\n                JFileChooser chooser = new JFileChooser();\n\n                chooser.setCurrentDirectory(new File(\".\"));\n                chooser.setDialogTitle(\"Please choose directory\");\n                chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);\n\n                int returnVal = chooser.showOpenDialog(null);\n                if (returnVal != JFileChooser.APPROVE_OPTION) {\n                    return (null);\n                }\n\n                path = chooser.getSelectedFile().getAbsolutePath();\n                File dir = new File(path);\n                String err = null;\n                if (!dir.exists()) {\n                    err = \"Directory [\" + path + \"] does not exist\";\n                } else if (!dir.isDirectory()) {\n                    err = \"Directory [\" + path + \"] is not a directory\";\n                }\n\n                if (err == null) {\n                    break;\n                }\n\n                Errmsg.getErrorHandler().notice(err);\n            }\n\n            return (path);\n        }\n\n        static String chooseFile() {\n\n\n            JFileChooser chooser = new JFileChooser();\n\n            chooser.setCurrentDirectory(new File(\".\"));\n            chooser.setDialogTitle(\"Please choose files\");\n            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n            int returnVal = chooser.showOpenDialog(null);\n            if (returnVal != JFileChooser.APPROVE_OPTION) {\n                return (null);\n            }\n\n            return chooser.getSelectedFile().getAbsolutePath();\n\n\n        }\n\n        /**\n         * return the panel's display name\n         */\n        public abstract String getPanelName();\n\n        /**\n         * save options from the UI to the preference store\n         */\n        public abstract void applyChanges();\n\n        /**\n         * load options from the preference store into the UI\n         */\n        public abstract void loadOptions();\n    }\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/PopupOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\nimport java.awt.Insets;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSpinner;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.border.Border;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.popup.ReminderListManager;\nimport net.sf.borg.ui.popup.ReminderManager;\nimport net.sf.borg.ui.popup.ReminderPopupManager;\nimport net.sf.borg.ui.popup.ReminderSound;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * The Class PopupOptionsPanel provies the options tab for editing popup\n * reminder options\n */\nclass PopupOptionsPanel extends OptionsPanel {\n\n\t/** The Constant serialVersionUID. */\n\tprivate static final long serialVersionUID = -5238561005974597879L;\n\n\t/** The checkfreq. */\n\tprivate final JSpinner checkfreq = new JSpinner();\n\n\t/** The number Of Reminder Times. */\n\tprivate int numberOfReminderTimes = 0;\n\n\t/** The popenablebox. */\n\tprivate final JCheckBox popenablebox = new JCheckBox();\n\n\tprivate final JCheckBox reminderListBox = new JCheckBox();\n\n\tprivate final JCheckBox taskReminderBox = new JCheckBox();\n\t\n\t//private final JCheckBox taskbarBox = new JCheckBox();\n\n\t/** The soundbox. */\n\tprivate final JComboBox<String> soundbox = new JComboBox<String>();\n\n\t/** The spinners for setting the reminder times */\n\tprivate final JSpinner[] spinners;\n\n\tprivate final JSpinner birthdayDays = new JSpinner();\n\n\t/**\n\t * Instantiates a new popup options panel.\n\t */\n\tpublic PopupOptionsPanel() {\n\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\tnumberOfReminderTimes = ReminderTimes.getNum();\n\n\t\tResourceHelper.setText(popenablebox, \"enable_popups\");\n\t\tthis.add(popenablebox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tJLabel jLabel15 = new JLabel();\n\t\tjLabel15.setText(Resource.getResourceString(\"todo_reminder_freq\"));\n\n\t\tthis.add(jLabel15,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\n\t\tcheckfreq.setMinimumSize(new java.awt.Dimension(50, 20));\n\t\tthis.add(checkfreq, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\t/*\n\t\t * sound\n\t\t */\n\t\tJPanel soundPanel = new JPanel();\n\t\tsoundPanel.setLayout(new GridBagLayout());\n\t\tsoundPanel\n\t\t\t\t.add(new JLabel(Resource.getResourceString(\"beep_options\")),\n\t\t\t\t\t\tGridBagConstraintsFactory.create(0, 0,\n\t\t\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tsoundbox.setEditable(true);\n\t\tsoundbox.addItem(Resource.getResourceString(\"default\"));\n\t\tsoundbox.addItem(Resource.getResourceString(\"no_sound\"));\n\t\tsoundbox.addItem(Resource.getResourceString(\"Use_system_beep\"));\n\t\tsoundbox.addItem(Resource.getResourceString(\"Browse\") + \"...\");\n\t\tsoundbox.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tif (soundbox.getSelectedIndex() == 3) {\n\t\t\t\t\tJFileChooser chooser = new JFileChooser();\n\t\t\t\t\tchooser.setCurrentDirectory(new File(\".\"));\n\t\t\t\t\tchooser.setDialogTitle(Resource\n\t\t\t\t\t\t\t.getResourceString(\"choose_file\"));\n\t\t\t\t\tchooser.setFileSelectionMode(JFileChooser.FILES_ONLY);\n\n\t\t\t\t\tint returnVal = chooser.showOpenDialog(null);\n\t\t\t\t\tif (returnVal != JFileChooser.APPROVE_OPTION)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\tString fileName = chooser.getSelectedFile()\n\t\t\t\t\t\t\t.getAbsolutePath();\n\t\t\t\t\tsoundbox.setSelectedItem(fileName);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t\tsoundPanel\n\t\t\t\t.add(soundbox, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tJButton play = new JButton();\n\t\tplay.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Forward16.gif\"));\n\t\tplay.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tReminderSound.playReminderSound(getSoundOption());\n\t\t\t}\n\n\t\t});\n\t\tsoundPanel\n\t\t\t\t.add(play, GridBagConstraintsFactory.create(2, 0,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tGridBagConstraints sPanelGBC = GridBagConstraintsFactory.create(0, 2,\n\t\t\t\tGridBagConstraints.VERTICAL);\n\t\tsPanelGBC.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tsPanelGBC.anchor = java.awt.GridBagConstraints.WEST;\n\t\tsPanelGBC.insets = new Insets(0, 0, 0, 0);\n\n\t\tthis.add(soundPanel, sPanelGBC);\n\n\t\tJPanel remTimePanel = new JPanel();\n\n\t\t// border\n\t\tString title = Resource.getResourceString(\"Popup_Times\") + \" (\"\n\t\t\t\t+ Resource.getResourceString(\"Minutes\") + \")\";\n\t\tBorder b = BorderFactory.createTitledBorder(remTimePanel.getBorder(),\n\t\t\t\ttitle);\n\t\tremTimePanel.setBorder(b);\n\n\t\tremTimePanel.setLayout(new GridLayout(4, 0));\n\n\t\t// add the spinners\n\t\tspinners = new JSpinner[numberOfReminderTimes];\n\t\tfor (int i = 0; i < numberOfReminderTimes; i++) {\n\t\t\tspinners[i] = new JSpinner(new SpinnerNumberModel(0, -99999, 99999,\n\t\t\t\t\t1));\n\t\t\tremTimePanel.add(spinners[i]);\n\t\t}\n\n\t\tGridBagConstraints remPanelGBC = GridBagConstraintsFactory.create(0, 3,\n\t\t\t\tGridBagConstraints.BOTH);\n\t\tremPanelGBC.gridwidth = java.awt.GridBagConstraints.REMAINDER;\n\t\tremPanelGBC.anchor = java.awt.GridBagConstraints.WEST;\n\n\t\tthis.add(remTimePanel, remPanelGBC);\n\n\t\treminderListBox.setText(Resource.getResourceString(\"reminder_list\"));\n\t\tthis.add(reminderListBox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 4, GridBagConstraints.BOTH));\n\n\t\ttaskReminderBox.setText(Resource\n\t\t\t\t.getResourceString(\"show_task_reminders\"));\n\t\tthis.add(taskReminderBox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 5, GridBagConstraints.BOTH));\n\t\t\n\t\t//taskbarBox.setText(Resource\n\t\t//\t\t.getResourceString(\"show_taskbar_reminders\"));\n\t\t//this.add(taskbarBox,\n\t\t//\t\tGridBagConstraintsFactory.create(0, 6, GridBagConstraints.BOTH));\n\t\t\n\n\t\tthis.add(new JLabel(Resource.getResourceString(\"bd_rem_days\")),\n\t\t\t\tGridBagConstraintsFactory.create(0, 7, GridBagConstraints.BOTH));\n\n\t\tbirthdayDays.setMinimumSize(new java.awt.Dimension(50, 20));\n\t\tthis.add(birthdayDays, GridBagConstraintsFactory.create(1, 7,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tOptionsPanel.setBooleanPref(popenablebox, PrefName.REMINDERS);\n\t\tOptionsPanel.setBooleanPref(taskReminderBox, PrefName.TASKREMINDERS);\n\t\t//OptionsPanel.setBooleanPref(taskbarBox, PrefName.TASKBAR_REMINDERS);\n\t\tPrefs.putPref(PrefName.BEEPINGREMINDERS, getSoundOption());\n\n\t\tInteger checkMins = (Integer) checkfreq.getValue();\n\t\tint cur = Prefs.getIntPref(PrefName.TODOREMINDERMINS);\n\t\tif (checkMins.intValue() != cur) {\n\t\t\t// why does this not save a new pref if the value is the same?\n\t\t\t// I no longer remeber if this matters - will leave as is\n\t\t\tPrefs.putPref(PrefName.TODOREMINDERMINS, checkMins);\n\t\t}\n\n\t\tInteger bddays = (Integer) birthdayDays.getValue();\n\t\tPrefs.putPref(PrefName.BIRTHDAYREMINDERDAYS, bddays);\n\n\t\tint[] arr = new int[numberOfReminderTimes];\n\t\tfor (int i = 0; i < numberOfReminderTimes; i++) {\n\t\t\tInteger ii = (Integer) spinners[i].getValue();\n\t\t\tarr[i] = ii.intValue();\n\t\t}\n\t\tReminderTimes.setTimes(arr);\n\t\tloadTimes();\n\n\t\t// restart reminder popups if the type of UI has changed\n\t\tboolean remlist = Prefs.getBoolPref(PrefName.REMINDERLIST);\n\t\tboolean newremlist = reminderListBox.isSelected();\n\t\tOptionsPanel.setBooleanPref(reminderListBox, PrefName.REMINDERLIST);\n\n\t\tif (remlist != newremlist) {\n\t\t\t// stop existing reminder UI\n\t\t\tReminderManager.getReminderManager().remove();\n\n\t\t\t// start new one\n\t\t\tif (newremlist == true) {\n\t\t\t\tReminderListManager.getReference();\n\t\t\t} else {\n\t\t\t\tReminderPopupManager.getReference();\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tOptionsPanel.setCheckBox(popenablebox, PrefName.REMINDERS);\n\t\tOptionsPanel.setCheckBox(reminderListBox, PrefName.REMINDERLIST);\n\t\tOptionsPanel.setCheckBox(taskReminderBox, PrefName.TASKREMINDERS);\n\t\t//OptionsPanel.setCheckBox(taskbarBox, PrefName.TASKBAR_REMINDERS);\n\n\t\tString beep = Prefs.getPref(PrefName.BEEPINGREMINDERS);\n\t\tif (beep.equals(\"true\")) {\n\t\t\tsoundbox.setSelectedIndex(0);\n\t\t} else if (beep.equals(\"false\")) {\n\t\t\tsoundbox.setSelectedIndex(1);\n\t\t} else if (beep.equals(\"system-beep\")) {\n\t\t\tsoundbox.setSelectedIndex(2);\n\t\t} else {\n\t\t\tsoundbox.setSelectedItem(beep);\n\t\t}\n\n\t\tint mins = Prefs.getIntPref(PrefName.TODOREMINDERMINS);\n\t\tcheckfreq.setValue(Integer.valueOf(mins));\n\n\t\tint bddays = Prefs.getIntPref(PrefName.BIRTHDAYREMINDERDAYS);\n\t\tbirthdayDays.setValue(Integer.valueOf(bddays));\n\n\t\t// load the times\n\t\tloadTimes();\n\n\t}\n\n\t/**\n\t * Load the spinner valies from stored prefs\n\t */\n\tprivate void loadTimes() {\n\t\tfor (int i = 0; i < numberOfReminderTimes; i++) {\n\t\t\tspinners[i].setValue(Integer.valueOf(ReminderTimes.getTimes(i)));\n\t\t}\n\t}\n\n\t/**\n\t * get the sound option preference value based on the current sound combo\n\t * box setting\n\t * \n\t * @return\n\t */\n\tprivate String getSoundOption() {\n\t\tString option = \"\";\n\t\tint i = soundbox.getSelectedIndex();\n\t\tif (i == 0) {\n\t\t\toption = \"true\";\n\t\t} else if (i == 1) {\n\t\t\toption = \"false\";\n\t\t} else if (i == 2) {\n\t\t\toption = \"system-beep\";\n\t\t} else {\n\t\t\toption = (String) soundbox.getSelectedItem();\n\t\t}\n\t\treturn option;\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"popup_reminders\");\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/StartupViewsOptionsPanel.java",
    "content": "\npackage net.sf.borg.ui.options;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.HashMap;\nimport java.util.Map.Entry;\n\n/**\n * Options for which views to open on startup\n * \n */\npublic class StartupViewsOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = -4357089819869820396L;\n\n\tstatic private PrefName getPrefForView( ViewType vt )\n\t{\n\t\tString def = \"false\";\n\t\tif( vt == ViewType.MONTH)\n\t\t\tdef = \"true\";\n\t\treturn new PrefName(\"startview_\" + vt.toString(), def);\n\t}\n\t\n\tstatic public boolean getStartPref(ViewType vt)\n\t{\n\t\treturn Prefs.getBoolPref(getPrefForView(vt));\n\t}\n\t\n\tstatic public void setStartPref(ViewType vt, boolean start)\n\t{\n\t\tPrefs.putPref(getPrefForView(vt), start ? \"true\" : \"false\");\n\t}\n\t\n\tprivate final HashMap<ViewType, JCheckBox> boxMap = new HashMap<ViewType, JCheckBox>();\n\t\n\t/**\n\t * Instantiates a new Todo Options Panel.\n\t */\n\tpublic StartupViewsOptionsPanel() {\n\t\tthis.setLayout(new GridLayout(0,2));\n\t\t\n\t\tfor( ViewType vt : MultiView.ViewType.values())\n\t\t{\n\t\t\tModule m = MultiView.getMainView().getModuleForView(vt);\n\t\t\tif( m != null )\n\t\t\t{\n\t\t\t\tJCheckBox cb = new JCheckBox(m.getModuleName());\n\t\t\t\tboxMap.put(vt, cb);\n\t\t\t\tthis.add(cb);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tfor( Entry<ViewType, JCheckBox> e : boxMap.entrySet() )\n\t\t{\n\t\t\tsetStartPref(e.getKey(), e.getValue().isSelected());\n\t\t}\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#getPanelName()\n\t */\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"startupViews\");\t\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tfor( Entry<ViewType, JCheckBox> e : boxMap.entrySet() )\n\t\t{\n\t\t\tboolean start = getStartPref(e.getKey());\n\t\t\te.getValue().setSelected(start);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/TaskOptionsPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003-2010 by Mike Berger\n */\npackage net.sf.borg.ui.options;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSpinner;\nimport javax.swing.SpinnerNumberModel;\nimport javax.swing.SwingConstants;\nimport javax.swing.border.TitledBorder;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * Provides the UI for editing Task options\n */\npublic class TaskOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = 1192889023921958906L;\n\tprivate final JCheckBox calShowSubtaskBox = new JCheckBox();\n\n\tprivate final JCheckBox calShowTaskBox = new JCheckBox();\n\tprivate final JCheckBox taskAbbrevBox = new JCheckBox();\n\tprivate final JCheckBox taskTreeStatusBox = new JCheckBox();\n\n\tprivate final JSpinner redSpinner = new JSpinner(new SpinnerNumberModel());\n\tprivate final JSpinner orangeSpinner = new JSpinner(new SpinnerNumberModel());\n\tprivate final JSpinner yellowSpinner = new JSpinner(new SpinnerNumberModel());\n\n\tprivate final ImageIcon redIcon = IconHelper.getIcon(\n\t\t\t\"/resource/red.png\");\n\tprivate final ImageIcon orangeIcon = IconHelper.getIcon(\n\t\t\t\"/resource/orange.png\");\n\tprivate final ImageIcon yellowIcon = IconHelper.getIcon(\n\t\t\t\"/resource/yellow.png\");\n\t\n\t/**\n\t * Instantiates a new task options panel.\n\t */\n\tpublic TaskOptionsPanel() {\n\n\t\tcalShowSubtaskBox.setName(\"calShowSubtaskBox\");\n\t\tcalShowSubtaskBox.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tcalShowTaskBox.setName(\"calShowTaskBox\");\n\t\tcalShowTaskBox.setHorizontalAlignment(SwingConstants.LEFT);\n\t\ttaskAbbrevBox.setName(\"taskAbbrevBox\");\n\t\ttaskAbbrevBox.setHorizontalAlignment(SwingConstants.LEFT);\n\t\ttaskTreeStatusBox.setName(\"taskTreeStatusBox\");\n\t\ttaskTreeStatusBox.setHorizontalAlignment(SwingConstants.LEFT);\n\n\t\tGridBagConstraints gridBagConstraints = GridBagConstraintsFactory\n\t\t\t\t.create(0, -1, GridBagConstraints.NONE);\n\t\tgridBagConstraints.anchor = GridBagConstraints.WEST;\n\n\t\tthis.setLayout(new GridBagLayout());\n\t\tthis.add(taskAbbrevBox, gridBagConstraints);\n\t\tthis.add(calShowTaskBox, gridBagConstraints);\n\t\tthis.add(calShowSubtaskBox, gridBagConstraints);\n\t\tthis.add(taskTreeStatusBox, gridBagConstraints);\n\n\t\ttaskAbbrevBox.setText(Resource.getResourceString(\"task_abbrev\"));\n\t\tcalShowTaskBox.setText(Resource.getResourceString(\"calShowTask\"));\n\t\tcalShowSubtaskBox.setText(Resource.getResourceString(\"calShowSubtask\"));\n\t\ttaskTreeStatusBox.setText(Resource\n\t\t\t\t.getResourceString(\"show_task_status_in_tree\"));\n\n\t\tJPanel spinnerPanel = new JPanel();\n\t\tspinnerPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"DaysLeftColors\")));\n\t\tspinnerPanel.setLayout(new GridLayout(4, 2));\n\n\t\tspinnerPanel.add(new JLabel(redIcon));\n\t\tspinnerPanel.add(redSpinner);\n\t\tspinnerPanel.add(new JLabel(orangeIcon));\n\t\tspinnerPanel.add(orangeSpinner);\n\t\tspinnerPanel.add(new JLabel(yellowIcon));\n\t\tspinnerPanel.add(yellowSpinner);\n\n\t\tthis.add(spinnerPanel, GridBagConstraintsFactory\n\t\t\t\t.create(0, -1, GridBagConstraints.HORIZONTAL));\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tOptionsPanel.setBooleanPref(taskAbbrevBox, PrefName.TASK_SHOW_ABBREV);\n\t\tOptionsPanel.setBooleanPref(calShowTaskBox, PrefName.CAL_SHOW_TASKS);\n\t\tOptionsPanel.setBooleanPref(calShowSubtaskBox,\n\t\t\t\tPrefName.CAL_SHOW_SUBTASKS);\n\t\tOptionsPanel.setBooleanPref(taskTreeStatusBox,\n\t\t\t\tPrefName.TASK_TREE_SHOW_STATUS);\n\t\t\n\t\tPrefs.putPref(PrefName.RED_DAYS, redSpinner.getValue());\n\t\tPrefs.putPref(PrefName.ORANGE_DAYS, orangeSpinner.getValue());\n\t\tPrefs.putPref(PrefName.YELLOW_DAYS, yellowSpinner.getValue());\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\n\t\tOptionsPanel.setCheckBox(taskAbbrevBox, PrefName.TASK_SHOW_ABBREV);\n\t\tOptionsPanel.setCheckBox(calShowTaskBox, PrefName.CAL_SHOW_TASKS);\n\t\tOptionsPanel.setCheckBox(calShowSubtaskBox, PrefName.CAL_SHOW_SUBTASKS);\n\t\tOptionsPanel.setCheckBox(taskTreeStatusBox,\n\t\t\t\tPrefName.TASK_TREE_SHOW_STATUS);\n\t\t\n\t\tredSpinner.setValue(Prefs.getIntPref(PrefName.RED_DAYS));\n\t\torangeSpinner.setValue(Prefs.getIntPref(PrefName.ORANGE_DAYS));\n\t\tyellowSpinner.setValue(Prefs.getIntPref(PrefName.YELLOW_DAYS));\n\t}\n\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"taskOptions\");\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/TodoOptionsPanel.java",
    "content": "/**\n *Created on Oct 24, 2010\n */\npackage net.sf.borg.ui.options;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.options.OptionsView.OptionsPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * Options for the Todo tab. Controls the behavior of the add quick todo items.\n * \n * @author thein\n * \n */\npublic class TodoOptionsPanel extends OptionsPanel {\n\n\tprivate static final long serialVersionUID = -4357089819869820396L;\n\n\t/**\n\t * If set, the text field is cleared on creation of a new todo item.\n\t */\n\tprivate final JCheckBox todoQuickEntryAutoClearTextField = new JCheckBox();\n\n\t/**\n\t * If set, the date field is defaulted to today's date initially and after\n\t * creating a new todo item.\n\t */\n\tprivate final JCheckBox todoQuickEntryAutoSetDateField = new JCheckBox();\n\t\n\n\t/**\n\t * Instantiates a new Todo Options Panel.\n\t */\n\tpublic TodoOptionsPanel() {\n\t\ttodoQuickEntryAutoClearTextField.setName(\"todoQuickEntryAutoClearTextField\");\n\t\ttodoQuickEntryAutoClearTextField.setHorizontalAlignment(SwingConstants.LEFT);\n\t\ttodoQuickEntryAutoSetDateField.setName(\"todoQuickEntryAutoSetDateField\");\n\t\ttodoQuickEntryAutoSetDateField.setHorizontalAlignment(SwingConstants.LEFT);\n\t\t\n\t\tGridBagConstraints gridBagConstraints = GridBagConstraintsFactory.create(0, -1, GridBagConstraints.NONE);\n\t\tgridBagConstraints.anchor = GridBagConstraints.WEST;\n\t\t\n\t\tthis.setLayout(new GridBagLayout());\n\t\tthis.add(todoQuickEntryAutoClearTextField, gridBagConstraints);\n\t\tthis.add(todoQuickEntryAutoSetDateField, gridBagConstraints);\n\t\n\t\ttodoQuickEntryAutoClearTextField.setText(Resource.getResourceString(\"todo_option_auto_clear_text\"));\n\t\ttodoQuickEntryAutoSetDateField.setText(Resource.getResourceString(\"todo_option_auto_date_today\"));\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#getPanelName()\n\t */\n\t@Override\n\tpublic String getPanelName() {\n\t\treturn Resource.getResourceString(\"todoOptions\");\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#applyChanges()\n\t */\n\t@Override\n\tpublic void applyChanges() {\n\t\tOptionsPanel.setBooleanPref(todoQuickEntryAutoClearTextField, PrefName.TODO_QUICK_ENTRY_AUTO_CLEAR_TEXT_FIELD);\n\t\tOptionsPanel.setBooleanPref(todoQuickEntryAutoSetDateField, PrefName.TODO_QUICK_ENTRY_AUTO_SET_DATE_FIELD);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.options.OptionsView.OptionsPanel#loadOptions()\n\t */\n\t@Override\n\tpublic void loadOptions() {\n\t\tOptionsPanel.setCheckBox(todoQuickEntryAutoClearTextField, PrefName.TODO_QUICK_ENTRY_AUTO_CLEAR_TEXT_FIELD);\n\t\tOptionsPanel.setCheckBox(todoQuickEntryAutoSetDateField, PrefName.TODO_QUICK_ENTRY_AUTO_SET_DATE_FIELD);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/options/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Provides the UI for editing Program Options\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  The UI package and its sub-packages provide the BORG Swing UI. The UI communicates with the database through the model classes.\n  The MultiView is the main BORG tabbed window.\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ApptReminderInstance.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2010 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.model.AppointmentModel;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.model.entity.Appointment;\nimport net.sf.borg.ui.calendar.AppointmentTextFormat;\n\nimport java.util.Date;\n\n/**\n * holds an instance of a reminder message. Keeps track of which reminder times\n * have been shown for this reminder message. Is not aware of the actual UI used\n * to display the reminder.\n */\npublic class ApptReminderInstance extends ReminderInstance {\n\n\t// the appointment\n\tprivate Appointment appt;\n\n\t/**\n\t * constructor\n\t * \n\t * @param appt\n\t *            the appointment\n\t * @param instanceTime\n\t *            the instance time.\n\t */\n\tpublic ApptReminderInstance(Appointment appt, Date instanceTime) {\n\t\tthis.appt = appt;\n\t\tthis.setInstanceTime(instanceTime);\n\n\t}\n\t\n\t/**\n\t * if the reminder instance is a todo, then mark it as done/complete\n\t */\n\t@Override\n\tpublic void do_todo(boolean delete)\n\t{\n\t\tif( appt != null )\n\t\t\ttry {\n\t\t\t\tAppointmentModel.getReference().do_todo(\n\t\t\t\t\t\tappt.getKey(), delete);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t}\n\t\n\t\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.lang.Object#equals(java.lang.Object)\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tApptReminderInstance other = (ApptReminderInstance) obj;\n\t\tif (appt == null) {\n\t\t\tif (other.appt != null)\n\t\t\t\treturn false;\n\t\t} else if (appt.getKey() != other.appt.getKey()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getInstanceTime() == null) {\n\t\t\treturn other.getInstanceTime() == null;\n\t\t} else return getInstanceTime().equals(other.getInstanceTime());\n\t}\n\t\n\t\n\t/**\n\t * get the index of the active reminder for this instance - i.e. latest one\n\t * passed\n\t * \n\t * @return index of reminder or -1 if none\n\t */\n\t@Override\n\tpublic int getCurrentReminder() {\n\n\t\t// get reminder timesd on/off flags for the appointment\n\t\t// default is all off if appt has a null value\n\t\tchar[] remTimes = new char[ReminderTimes.getNum()];\n\t\ttry {\n\t\t\tremTimes = (appt.getReminderTimes()).toCharArray();\n\t\t} catch (Exception e) {\n\t\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\t\tremTimes[i] = 'N';\n\t\t\t}\n\t\t}\n\n\t\t// determine how far away the appt is\n\t\tlong minutesToGo = getInstanceTime().getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\n\t\t// determine which reminder is next\n\t\tint nextFutureReminder = 0;\n\t\twhile (ReminderTimes.getTimes(nextFutureReminder) < minutesToGo) {\n\t\t\t++nextFutureReminder;\n\t\t}\n\n\t\t// shouldn't happen\n\t\tif (nextFutureReminder >= ReminderTimes.getNum()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (remTimes[nextFutureReminder] == 'Y') {\n\t\t\treturn nextFutureReminder;\n\t\t}\n\n\t\t// none found\n\t\treturn -1;\n\t}\n\n\t/**\n\t * get the text for the reminder\n\t * @return the reminder text\n\t */\n\t@Override\n\tpublic String getText()\n\t{\n\t\tif( appt != null )\n\t\t\treturn AppointmentTextFormat.format(appt,getInstanceTime());\n\t\treturn null;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.lang.Object#hashCode()\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((appt == null) ? 0 : appt.getKey());\n\t\tresult = prime * result\n\t\t\t\t+ ((getInstanceTime() == null) ? 0 : getInstanceTime().hashCode());\n\t\treturn result;\n\t}\n\n\n\t/**\n\t * return true if the reminder is a note (is untimed)\n\t */\n\t@Override\n\tpublic boolean isNote()\n\t{\n\t\tif( appt != null )\n\t\t\treturn AppointmentModel.isNote(appt);\n\t\treturn false;\n\t}\n\n\t/**\n\t * determine if an appointment time is outside of the range of its reminder\n\t * times.\n\t * \n\t * @return true, if the appointment tome is not in range of its reminder\n\t *         times\n\t */\n\tprivate boolean isOutsideOfReminderTimes(boolean nonExpiring) {\n\n\t\t// determine how far away the appt is\n\t\tlong minutesToGo = getInstanceTime().getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\n\t\tint latestReminderTime = 100000;\n\t\tint earliestReminderTime = -100000;\n\t\tchar[] remTimes = new char[ReminderTimes.getNum()];\n\t\ttry {\n\t\t\tremTimes = (appt.getReminderTimes()).toCharArray();\n\n\t\t} catch (Exception e) {\n\t\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\t\tremTimes[i] = 'N';\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); i++) {\n\t\t\tif (remTimes[i] == 'Y') {\n\t\t\t\tint time = ReminderTimes.getTimes(i);\n\t\t\t\tif (time > earliestReminderTime) {\n\t\t\t\t\tearliestReminderTime = ReminderTimes.getTimes(i);\n\t\t\t\t}\n\t\t\t\tif (time < latestReminderTime) {\n\t\t\t\t\tlatestReminderTime = ReminderTimes.getTimes(i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (earliestReminderTime == -100000)\n\t\t\treturn true;\n\n\t\treturn (minutesToGo > earliestReminderTime || (!nonExpiring && minutesToGo < latestReminderTime));\n\t}\n\n\t/**\n\t * return true if the reminder is a todo - either an appointment todo or another type of todo, such as a task\n\t */\n\t@Override\n\tpublic boolean isTodo()\n\t{\n\t\tif( appt != null )\n\t\t\treturn appt.isTodo();\n\t\treturn false;\n\t}\n\n\t/**\n\t * reload the model entity from the db and check if it has changed.\n\t * @return true if the entity has changed in a way that affects reminders\n\t */\n\t@Override\n\tpublic boolean reloadAndCheckForChanges()\n\t{\n\t\ttry {\n\t\t\tAppointment origAppt = appt;\n\t\t\tappt = AppointmentModel.getReference().getAppt(\n\t\t\t\t\tappt.getKey());\n\t\t\tif (appt == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (!appt.getDate()\n\t\t\t\t\t.equals(origAppt.getDate())) {\n\t\t\t\t// date changed - delete. new instance will be added on\n\t\t\t\t// periodic update\n\t\t\t\treturn true;\n\t\t\t}\n\n\n\t\t\t// delete it if the text changed - will be added back in\n\t\t\t// periodic check for\n\t\t\t// popups\n\t\t\tif (!appt.getText()\n\t\t\t\t\t.equals(origAppt.getText())) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (isTodo()) {\n\t\t\t\t// skip if inst time changed for untimed todos\n\t\t\t\tDate nt = appt.getNextTodo();\n\t\t\t\tif (nt == null)\n\t\t\t\t\tnt = appt.getDate();\n\n\t\t\t\tif (!getInstanceTime().equals(nt)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\n\t\t\t// appt cannot be read, must have been deleted\n\t\t\t// this is an expected case when appointments are deleted\n\t\t\tappt = null;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n\n\t/**\n\t * determine if an appointment popup should be shown for an appointment that\n\t * doesn't yet have a popup associated with it\n\t * \n\t * @return true, if successful\n\t */\n\t@Override\n\tpublic boolean shouldBeShown() {\n\t\t\n\t\tif( appt == null )\n\t\t\treturn false;\n\n\t\t// check if we should show it based on public/private\n\t\t// flags\n\t\tboolean showpub = true;//Prefs.getBoolPref(PrefName.SHOWPUBLIC);\n\n\t\tboolean showpriv = Prefs.getBoolPref(PrefName.SHOWPRIVATE);\n\n\t\tif (appt.isPrivate()) {\n\t\t\tif (!showpriv)\n\t\t\t\treturn false;\n\t\t} else {\n\t\t\tif (!showpub)\n\t\t\t\treturn false;\n\t\t}\n\n\t\t// don't popup untimed appointments that are not todos\n\t\tif (isNote() && !isTodo())\n\t\t\treturn false;\n\n\t\tboolean expires = !isTodo()\n\t\t\t\t|| appt.getReminderTimes() == null\n\t\t\t\t|| appt.getReminderTimes().indexOf('Y') == -1; // true if the reminder eventually stops at some\n\t\t// point after the appt\n\n\t\t// todos never expire\n\n\t\t// a normal timed appt only gets a popup\n\t\t// if it's within its range of reminder times\n\t\treturn !isOutsideOfReminderTimes(!expires);\n\t}\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/BirthdayReminderInstance.java",
    "content": "package net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.AddressModel;\nimport net.sf.borg.model.entity.Address;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * holds reminders based on birthdays in the address book\n *\n */\npublic class BirthdayReminderInstance extends ReminderInstance {\n\n\tprivate Address addr;\n\t\n\t/**\n\t * constructor\n\t * @param addr - the address\n\t * @param instanceTime - the birthday instance time\n\t */\n\tpublic BirthdayReminderInstance(Address addr, Date instanceTime) {\n\t\tthis.addr = addr;\n\t\tthis.setInstanceTime(instanceTime);\n\t}\n\n\t@Override\n\tpublic void do_todo(boolean delete) {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tBirthdayReminderInstance other = (BirthdayReminderInstance) obj;\n\t\tif (addr == null) {\n\t\t\tif (other.addr != null)\n\t\t\t\treturn false;\n\t\t} else if (addr.getKey() != other.addr.getKey()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getInstanceTime() == null) {\n\t\t\treturn other.getInstanceTime() == null;\n\t\t} else return getInstanceTime().equals(other.getInstanceTime());\n\t}\n\n\t@Override\n\tpublic int getCurrentReminder() {\n\t\t// birthdays do not use the set reminder times\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\tGregorianCalendar inst = new GregorianCalendar();\n\t\tinst.setTime(getInstanceTime());\n\t\t\n\t\tDate bd = addr.getBirthday();\n\t\tGregorianCalendar g = new GregorianCalendar();\n\t\tg.setTime(bd);\n\t\tint bdyear = g.get(Calendar.YEAR);\n\t\tint yrs = inst.get(Calendar.YEAR) - bdyear;\n\n\t\tString tx = Resource.getResourceString(\"Birthday\") + \": \"\n\t\t\t\t+ addr.getFirstName() + \" \" + addr.getLastName() + \"(\"\n\t\t\t\t+ yrs + \")\";\n\t\treturn tx;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((addr == null) ? 0 : addr.getKey());\n\t\tresult = prime * result\n\t\t\t\t+ ((getInstanceTime() == null) ? 0 : getInstanceTime().hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String calculateToGoMessage() {\n\t\treturn Resource.getResourceString(\"Birthday\");\n\t}\n\n\t@Override\n\tpublic boolean isNote() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTodo() {\n\t\t// treat as todo for determining when to display\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean reloadAndCheckForChanges() {\n\t\ttry {\n\t\t\tAddress orig = addr;\n\t\t\taddr = AddressModel.getReference().getAddress(addr.getKey());\n\t\t\tif (addr == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif (addr.getBirthday() == null)\n\t\t\t\treturn true;\n\n\t\t\tif (!addr.getBirthday()\n\t\t\t\t\t.equals(orig.getBirthday())) {\n\t\t\t\t// date changed - delete. new instance will be added on\n\t\t\t\t// periodic update\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t} catch (Exception e) {\n\n\t\t\t// cannot be read, must have been deleted\n\t\t\t// this is an expected case when items are deleted\n\t\t\taddr = null;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean shouldBeShown() {\n\t\tif( addr == null || addr.getBirthday() == null )\n\t\t\treturn false;\n\n\t\t// determine how far away the task is\n\t\tlong minutesToGo = getInstanceTime().getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\n\t\tif( minutesToGo < 0) return false;\n\t\t\n\t\tint bd_days = Prefs.getIntPref(PrefName.BIRTHDAYREMINDERDAYS);\n\t\tbd_days += 1; // 0 means show on the current day, 1 means 1 day before - so need to increment bd_days\n\t\t\n\t\treturn (minutesToGo < bd_days*24*60);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/PopupOptionsView.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.popup;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComponent;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.KeyStroke;\n\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * PopupOptionsView displays a dialog that lets the user choose\n * which reminder times to activate or deactivate\n */\npublic class PopupOptionsView extends JDialog {\n\t\n\t/**\n\t * interface implemented by the component that is waiting for the\n\t * resulting popup times string\n\t */\n\tpublic interface PopupOptionsListener {\n\t\tvoid setPopupTimesString(String popupTimesString);\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The reminder time check boxes. */\n\tprivate JCheckBox[] reminderTimeCheckBoxes;\n\t\n\t/** The parent component */\n\tprivate final PopupOptionsListener popupOptionsListener;\n\t\n\t/** The reminder times on off array. */\n\tprivate final char[] reminderTimesOnOffArray;\n\n\t/**\n\t * Instantiates a new popup options view.\n\t * \n\t * @param remtimes the reminder times Y/N (on/off) flags from the appointment\n\t * @param appPanel the appointment panel \n\t */\n\tpublic PopupOptionsView(String remtimes, String appointmentName, PopupOptionsListener popupOptionsListener) {\n\t\tsuper();\n\t\t\n\t\tthis.popupOptionsListener = popupOptionsListener;\n\t\treminderTimesOnOffArray = remtimes.toCharArray();\n\n\t\tinitialize(appointmentName);\n\n\t\t// escape key closes window\n\t\tgetRootPane().registerKeyboardAction(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic final void actionPerformed(ActionEvent e) {\n\t\t\t\tdispose();\n\t\t\t}\n\t\t}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\n\t\t\n\t\tthis.setTitle(\"Popup_Times\");\n\t\t\n\t\tpack();\n\n\t\tthis.setModal(true);\n\n\t}\n\n\t/**\n\t * Creates the button panel.\n\t * \n\t * @return the button panel\n\t */\n\tprivate JPanel createButtonPanel() {\n\t\tJPanel buttonPanel = new JPanel();\n\n\t\tJButton saveButton = new JButton();\n\t\tResourceHelper.setText(saveButton, \"Save\");\n\t\tsaveButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Save16.gif\"));\n\t\tsaveButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\n\t\t\t\t// set the remidner times array from the check box values\n\t\t\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\t\t\tif (reminderTimeCheckBoxes[i].isSelected()) {\n\t\t\t\t\t\treminderTimesOnOffArray[i] = 'Y';\n\t\t\t\t\t} else {\n\t\t\t\t\t\treminderTimesOnOffArray[i] = 'N';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// save the changes to the appointment panel\n\t\t\t\tpopupOptionsListener.setPopupTimesString(new String(reminderTimesOnOffArray));\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(saveButton, null);\n\n\t\tJButton clearAllButton = new JButton();\n\t\tResourceHelper.setText(clearAllButton, \"clear_all\");\n\t\tclearAllButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\t\t\treminderTimeCheckBoxes[i].setSelected(false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(clearAllButton, null);\n\t\t\n\t\tJButton selectAllButton = new JButton();\n\t\tResourceHelper.setText(selectAllButton, \"select_all\");\n\t\tselectAllButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\t\t\treminderTimeCheckBoxes[i].setSelected(true);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(selectAllButton, null); \n\n\t\tJButton dismissButton = new JButton();\n\t\tResourceHelper.setText(dismissButton, \"Dismiss\");\n\t\tdismissButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Stop16.gif\"));\n\t\tdismissButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\t\t\n\t\tbuttonPanel.add(dismissButton, null);\n\t\treturn buttonPanel;\n\t}\n\n\t/**\n\t * This method initializes the UI.\n\t * \n\t * @param appointmentName the appointment name (title)\n\t */\n\tprivate void initialize(String appointmentName) {\n\t\t\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\t\n\t\tJPanel checkBoxPanel = new JPanel();\n\t\tcheckBoxPanel.setLayout(new GridBagLayout());\n\t\t\n\t\tJLabel jAlarmLabel = new JLabel();\n\t\tResourceHelper.setText(jAlarmLabel, \"custom_times_header\");\n\t\tjAlarmLabel.setText(jAlarmLabel.getText() + \" '\" + appointmentName\n\t\t\t\t+ \"'\");\n\t\tcheckBoxPanel.add(jAlarmLabel, new GridBagConstraints(0, 0, 2, 1, 0.0,\n\t\t\t\t0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,\n\t\t\t\tnew Insets(4, 4, 4, 4), 0, 0));\n\n\t\t// create reminder time check boxes\n\t\treminderTimeCheckBoxes = new JCheckBox[ReminderTimes.getNum()];\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\treminderTimeCheckBoxes[i] = new JCheckBox(minutes_string(i));\n\t\t}\n\t\t\n\t\t// add boxes in 2 columns\n\t\tint boxesPerColumn = reminderTimeCheckBoxes.length / 2;\n\t\t\n\t\t// left column\n\t\tfor (int i = 0; i < boxesPerColumn; ++i) {\n\t\t\tcheckBoxPanel.add(reminderTimeCheckBoxes[i], new GridBagConstraints(0, i + 1, 1,\n\t\t\t\t\t1, 0.0, 0.0, GridBagConstraints.WEST,\n\t\t\t\t\tGridBagConstraints.BOTH, new Insets(4, 4, 4, 4), 0, 0));\n\n            reminderTimeCheckBoxes[i].setSelected(reminderTimesOnOffArray[i] == 'Y');\n\t\t}\n\n\t\t// right column\n\t\tfor (int i = boxesPerColumn; i < reminderTimeCheckBoxes.length; ++i) {\n\t\t\tcheckBoxPanel.add(reminderTimeCheckBoxes[i], new GridBagConstraints(1, i - boxesPerColumn\n\t\t\t\t\t+ 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,\n\t\t\t\t\tGridBagConstraints.BOTH, new Insets(4, 4, 4, 4), 0, 0));\n            reminderTimeCheckBoxes[i].setSelected(reminderTimesOnOffArray[i] == 'Y');\n\t\t}\n\t\t\n\t\ttopPanel.add(checkBoxPanel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\t\ttopPanel.add(createButtonPanel(), GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 0.0, 1.0));\n\n\t\tthis.setContentPane(topPanel);\n\t}\n\n\t/**\n\t * generate a human readable string for a particular reminder time\n\t * \n\t * @param i the index of the remidner time\n\t * \n\t * @return the string\n\t */\n\tprivate static String minutes_string(int i) {\n\t\t\n\t\tint reminderTimeMinutes = ReminderTimes.getTimes(i);\n\t\tint reminderTimeMinutesAbsoluteValue = (reminderTimeMinutes >= 0 ? reminderTimeMinutes : -reminderTimeMinutes);\n\t\tint reminderTimeHours = reminderTimeMinutesAbsoluteValue / 60;\n\t\tint reminderTimeMinutesPastTheHour = reminderTimeMinutesAbsoluteValue % 60;\n\t\t\n\t\tString minutesString;\n\t\tString hoursString;\n\t\t\n\t\tif (reminderTimeHours > 1) {\n\t\t\thoursString = reminderTimeHours + \" \" + Resource.getResourceString(\"Hours\");\n\t\t} else if (reminderTimeHours > 0) {\n\t\t\thoursString = reminderTimeHours + \" \" + Resource.getResourceString(\"Hour\");\n\t\t} else {\n\t\t\thoursString = \"\";\n\t\t}\n\n\t\tif (reminderTimeMinutesPastTheHour > 1) {\n\t\t\tminutesString = reminderTimeMinutesPastTheHour + \" \" + Resource.getResourceString(\"Minutes\");\n\t\t} else if (reminderTimeMinutesPastTheHour > 0) {\n\t\t\tminutesString = reminderTimeMinutesPastTheHour + \" \" + Resource.getResourceString(\"Minute\");\n\t\t} else if (reminderTimeHours >= 1) {\n\t\t\tminutesString = \"\";\n\t\t} else {\n\t\t\tminutesString = reminderTimeMinutesPastTheHour + \" \" + Resource.getResourceString(\"Minutes\");\n\t\t}\n\n\t\t// space between hours and minutes\n\t\tif (!hoursString.equals(\"\") && !minutesString.equals(\"\"))\n\t\t\tminutesString = \" \" + minutesString;\n\n\t\tString beforeOrAfterString;\n\t\tif (reminderTimeMinutes > 0) {\n\t\t\tbeforeOrAfterString = \" \" + Resource.getResourceString(\"Before\");\n\t\t} else if (reminderTimeMinutes == 0) {\n\t\t\tbeforeOrAfterString = \"\";\n\t\t} else {\n\t\t\tbeforeOrAfterString = \" \" + Resource.getResourceString(\"After\");\n\t\t}\n\n\t\treturn hoursString + minutesString + beforeOrAfterString;\n\t}\n\n} \n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ProjectReminderInstance.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2011 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Project;\n\nimport java.util.Date;\n\n/**\n * A Reminder Instance class that holds a Project\n * \n */\npublic class ProjectReminderInstance extends ReminderInstance {\n\n\tprivate Project project;\n\n\t@Override\n\tpublic void do_todo(boolean delete) {\n\t\ttry {\n\t\t\tTaskModel.getReference().closeProject(project.getKey());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tProjectReminderInstance other = (ProjectReminderInstance) obj;\n\t\tif (project == null) {\n\t\t\tif (other.project != null)\n\t\t\t\treturn false;\n\t\t} else if (project.getKey() != other.project.getKey()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getInstanceTime() == null) {\n\t\t\treturn other.getInstanceTime() == null;\n\t\t} else return getInstanceTime().equals(other.getInstanceTime());\n\t}\n\n\t@Override\n\tpublic int getCurrentReminder() {\n\t\treturn -1;\n\t}\n\n\t/**\n\t * @param project\n\t */\n\tpublic ProjectReminderInstance(Project project) {\n\t\tsuper();\n\t\tthis.project = project;\n\t\tthis.setInstanceTime(project.getDueDate());\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn project.getText();\n\t}\n\n\t@Override\n\tpublic String calculateToGoMessage() {\n\t\treturn Resource.getResourceString(\"project\");\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((project == null) ? 0 : project.getKey());\n\t\tresult = prime\n\t\t\t\t* result\n\t\t\t\t+ ((getInstanceTime() == null) ? 0 : getInstanceTime()\n\t\t\t\t\t\t.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean isNote() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTodo() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean reloadAndCheckForChanges() {\n\t\ttry {\n\t\t\tProject orig = project;\n\t\t\tproject = TaskModel.getReference().getProject(project.getKey());\n\t\t\tif (project == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif( TaskModel.isClosed(project))\n\t\t\t\treturn true;\n\n\t\t\tif (!project.getDueDate()\n\t\t\t\t\t.equals(orig.getDueDate())) {\n\t\t\t\t// date changed - delete. new instance will be added on\n\t\t\t\t// periodic update\n\t\t\t\treturn true;\n\t\t\t}\n\n\n\t\t\t// delete it if the text changed - will be added back in\n\t\t\t// periodic check for\n\t\t\t// popups\n\t\t\tif (!project.getDescription()\n\t\t\t\t\t.equals(orig.getDescription())) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t\n\t\t} catch (Exception e) {\n\n\t\t\t// project cannot be read, must have been deleted\n\t\t\t// this is an expected case when appointments are deleted\n\t\t\tproject = null;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean shouldBeShown() {\n\t\t\n\t\tif( !Prefs.getBoolPref(PrefName.TASKREMINDERS))\n\t\t\treturn false;\n\t\t\n\t\tif( project == null || TaskModel.isClosed(project) )\n\t\t\treturn false;\n\n\t\t// determine how far away the project is\n\t\tDate d = DateUtil.setToMidnight(getInstanceTime());\n\t\tlong minutesToGo =d.getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\t\tint earliestReminderTime = -100000;\n\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); i++) {\n\t\t\tint time = ReminderTimes.getTimes(i);\n\t\t\tif (time > earliestReminderTime) {\n\t\t\t\tearliestReminderTime = time;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif( earliestReminderTime == -100000)\n\t\t\treturn false;\n\n\t\treturn (minutesToGo < earliestReminderTime);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderInstance.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2010 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport lombok.Data;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\n\nimport java.util.Date;\n\n/**\n * holds an instance of a reminder message. Keeps track of which reminder times\n * have been shown for this reminder message. Is not aware of the actual UI used\n * to display the reminder.\n */\n@Data\nabstract public class ReminderInstance {\n\n\t/**\n\t * was the reminder hidden by the user\n\t */\n\tprivate boolean hidden = false;\n\n\t// the instance time - this is the time when this instance occurs. this\n\t// will be\n\t// different from the entity time if this instance is a repeat of an\n\t// appointment\n\tprivate Date instanceTime;\n\n\t/** The reminders shown flags - tracks which reminder times have been shown */\n\tprivate char[] remindersShown;\n\n\t/**\n\t * The was ever shown flag - used to decide if the reminder needs to be\n\t * shown when the program is first starting up,but we are not on a todo\n\t * reminder time\n\t */\n\tprivate boolean shown = false;\n\n\tpublic ReminderInstance() {\n\n\t\t// initialize reminders shown flags\n\t\tsetRemindersShown(new char[ReminderTimes.getNum()]);\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); ++i) {\n\t\t\tgetRemindersShown()[i] = 'N';\n\t\t}\n\n\t}\n\t\n\t/**\n\t * calculate the to go message for a reminder instance\n\t * \n\t * @return the to go message or null if should not be shown\n\t */\n\tpublic String calculateToGoMessage() {\n\n\n\t\tString message = null;\n\n\t\t// set the reminder due message\n\t\tif (isNote() && isTodo()) {\n\t\t\tmessage = Resource.getResourceString(\"To_Do\");\n\t\t} else {\n\n\t\t\t// timed appt\n\t\t\tDate d = getInstanceTime();\n\t\t\tif (d == null)\n\t\t\t\treturn null;\n\n\t\t\t// get the reminder time and also mark the reminder as shown\n\t\t\t// since w are adding it to the UI\n\t\t\t// it may already have been marked as shown - no problem\n\t\t\tint reminderIndex = getCurrentReminder();\n\t\t\tif (reminderIndex != -1) {\n\t\t\t\tmarkAsShown(reminderIndex);\n\t\t\t}\n\n\t\t\tint minutesToGo = minutesToGo();\n\n\t\t\tString timeString = \"\";\n\t\t\tif (minutesToGo != 0) {\n\t\t\t\tint absmin = Math.abs(minutesToGo);\n\t\t\t\tint days = absmin / (24 * 60);\n\t\t\t\tint hours = (absmin % (24 * 60)) / 60;\n\t\t\t\tint mins = (absmin % 60);\n\n\t\t\t\tif (days > 0)\n\t\t\t\t\ttimeString += days + \" \"\n\t\t\t\t\t\t\t+ Resource.getResourceString(\"Days\") + \" \";\n\t\t\t\tif (hours > 0)\n\t\t\t\t\ttimeString += hours + \" \"\n\t\t\t\t\t\t\t+ Resource.getResourceString(\"Hours\") + \" \";\n\t\t\t\ttimeString += Integer.toString(mins);\n\n\t\t\t}\n\n\t\t\t// create a message saying how much time to go there is\n\t\t\tif (minutesToGo < 0) {\n\t\t\t\tmessage = timeString + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"minutes_ago\");\n\t\t\t} else if (minutesToGo == 0) {\n\t\t\t\tmessage = Resource.getResourceString(\"Now\");\n\t\t\t} else {\n\t\t\t\tmessage = timeString + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"Minutes\");\n\t\t\t}\n\n\t\t}\n\n\t\treturn message;\n\n\t}\n\t\n\t/**\n\t * if the reminder instance is a todo, then mark it as done/complete\n\t */\n\tabstract public void do_todo(boolean delete);\n\t\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.lang.Object#equals(java.lang.Object)\n\t */\n\t/**\n\t * determine if a reminder is ready to be popped up and return the number of\n\t * the reminder that is ready\n\t * \n\t * @return the reminder number or -1 if not ready for a new popup\n\t * \n\t */\n\tfinal public int dueForPopup() {\n\n\t\tint index = getCurrentReminder();\n\n\t\t// if the current reminder has already been shown, then we should not\n\t\t// trigger a new\n\t\t// popup - it is either already showing or has been hidden\n\n\t\tif (index >= 0 && getRemindersShown()[index] == 'N') {\n\t\t\treturn index;\n\t\t}\n\n\t\t// not due for popup\n\t\treturn -1;\n\t}\n\t\n\t\n\t@Override\n\tabstract public boolean equals(Object obj);\n\t\n\n\t/**\n\t * get the index of the active reminder for this instance - i.e. latest one\n\t * passed\n\t * \n\t * @return index of reminder or -1 if none\n\t */\n\tabstract public int getCurrentReminder();\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.lang.Object#hashCode()\n\t */\n\t/**\n\t * get the text for the reminder\n\t * @return the reminder text\n\t */\n\tabstract public String getText();\n\n\n\t@Override\n\tabstract public int hashCode();\n\n\t\n\t/**\n\t * return true if the reminder is a note (is untimed)\n\t */\n\tabstract public boolean isNote();\n\t\n\n\t/**\n\t * return true if the reminder is a todo - either an appointment todo or another type of todo, such as a task\n\t */\n\tabstract public boolean isTodo();\n\n\t/**\n\t * mark a reminder time as shown\n\t * \n\t * @param reminderNumber\n\t *            the reminder time index\n\t */\n\tfinal public void markAsShown(int reminderNumber) {\n\t\tgetRemindersShown()[reminderNumber] = 'Y';\n\t}\n\t\n\n\tpublic int minutesToGo() {\n\t\treturn (int) ((getInstanceTime().getTime() / (60 * 1000) - new Date()\n\t\t\t\t.getTime()\n\t\t\t\t/ (60 * 1000)));\n\t}\n\t\n\t\n\t/**\n\t * reload the model entity from the db and check if it has changed.\n\t * @return true if the entity has changed in a way that affects reminders\n\t */\n\tabstract public boolean reloadAndCheckForChanges();\n\n\n\t/**\n\t * determine if an appointment popup should be shown for an appointment that\n\t * doesn't yet have a popup associated with it\n\t * \n\t * @return true, if successful\n\t */\n\tabstract public boolean shouldBeShown();\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderList.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2010 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.swing.Icon;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.DefaultTableCellRenderer;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.View;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * ReminderList is a UI to show the list of reminders managed by the\n * ReminderListManager. It shows all reminder messages in a single table.\n * \n * @author mbb\n * \n */\npublic class ReminderList extends View {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate static final PrefName REMINDERLISTSIZE = new PrefName(\"reminderlistsize\",\n\t\"-1,-1,-1,-1,N\");\n\n\n\tprivate JPanel buttonPanel = new JPanel();\n\n\tprivate JTable reminderTable = new JTable();\n\n\tprivate JCheckBox quietBox = new JCheckBox();\n\t\n\t// table columns\n\tstatic private final int SELECT_COLUMN = 0;\n\tstatic private final int TEXT_COLUMN = 1;\n\tstatic private final int TOGO_MESSAGE_COLUMN = 2;\n\tstatic private final int REMINDER_INSTANCE_COLUMN = 3;\n\tstatic private final int TOGO_MINUTES_COLUMN = 4;\n\t\n\t/**\n\t * renderer to add the todo marker to the reminder text in the table\n\t *\n\t */\n\tstatic private class MyTableCellRenderer extends DefaultTableCellRenderer {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table, Object value,\n\t    \t\tboolean isSelected, boolean hasFocus, int row, int column) {\n\n\t    \tJLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\n\n\t\t\tlabel.setIcon(null);\n\n\t    \tif(column == TEXT_COLUMN){\n\t    \t\t\t    \t\t\n\t    \t\tReminderInstance inst = (ReminderInstance)value;\n\t    \t\t\n\t    \t\t// get appt text\n\t\t\t\tString tx = DateFormat.getDateInstance(DateFormat.SHORT).format(\n\t\t\t\t\t\tinst.getInstanceTime());\n\n\t\t\t\ttx += \" \" + inst.getText();\n\t\t\t\t\n\t\t\t\tlabel.setText(tx);\n\n\t    \t\t// add todo icon if needed\n\t\t\t\tif( inst.isTodo())\n\t\t\t\t{\n\t\t\t\t\tString iconname = Prefs.getPref(PrefName.UCS_MARKER);\n\t\t\t\t\tString use_marker = Prefs.getPref(PrefName.UCS_MARKTODO);\n\t\t\t\t\tif (use_marker.equals(\"true\")) {\n\t\t\t\t\t\tif (iconname.endsWith(\".gif\") || iconname.endsWith(\".jpg\")) {\n\t\t\t\t\t\t\tIcon todoIcon = IconHelper.getIcon(\n\t\t\t\t\t\t\t\t\t\"/resource/\" + iconname);\n\t\t\t\t\t\t\tlabel.setIcon(todoIcon);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlabel.setText(iconname + \" \" + tx);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t    \t}\n\t    \treturn label;\n\t    }\n\t}\n\n\tpublic ReminderList() {\n\t\tsuper();\n\n\t\tinitComponents();\n\n\t\tthis.setTitle(\"Borg \" + Resource.getResourceString(\"Reminder\"));\n\n\t\t// inst is the ReminderInstance in a hidden column\n\t\t// mins is the minutes-to-go of the reminder - for sorting. hidden\n\t\t// column.\n\t\treminderTable.setModel(new TableSorter(new String[] { \"\",\n\t\t\t\tResource.getResourceString(\"Reminder\"),\n\t\t\t\tResource.getResourceString(\"Due\"), \"inst\", \"mins\" },\n\t\t\t\tnew Class[] { Boolean.class, String.class, String.class,\n\t\t\t\t\t\tReminderInstance.class, Long.class }, \n\t\t\t\t\t\tnew boolean[]{ true, false, false, false, false}));\n\n\t\t// hide inst and mins columns\n\t\treminderTable.removeColumn(reminderTable.getColumnModel().getColumn(3));\n\t\treminderTable.removeColumn(reminderTable.getColumnModel().getColumn(3));\n\t\t\n\t\treminderTable.getColumnModel().getColumn(ReminderList.SELECT_COLUMN).setMaxWidth(30);\n\t\treminderTable.getColumnModel().getColumn(ReminderList.SELECT_COLUMN).setMinWidth(20);\n\t\t\n\t\treminderTable.setDefaultRenderer(String.class, new MyTableCellRenderer());\n\t\treminderTable.getTableHeader().setReorderingAllowed(false);\n\t\treminderTable.setShowGrid(true);\n\t\treminderTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tpack();\n\n\t\tmanageMySize(REMINDERLISTSIZE);\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * Gets the selected ReminderInstances.\n\t * \n\t * @return a List of selected reminder instances\n\t */\n\tprivate List<ReminderInstance> getSelectedReminders() {\n\t\n\t\tList<ReminderInstance> list = new ArrayList<ReminderInstance>();\n\t\t\n\t\tTableSorter tm = (TableSorter) reminderTable.getModel();\n\n\t\tfor( int row = 0; row < tm.getRowCount(); row++ )\n\t\t{\n\t\t\tBoolean selected = (Boolean)tm.getValueAt(row, ReminderList.SELECT_COLUMN);\n\t\t\tif( selected.booleanValue() == true)\n\t\t\t{\n\t\t\t\tlist.add((ReminderInstance)tm.getValueAt(row, ReminderList.REMINDER_INSTANCE_COLUMN));\n\t\t\t}\n\t\t\t\t\n\t\t}\n\t\t\n\t\treturn list;\n\t\n\t}\n\n\tprivate void initComponents() {\n\n\t\tthis.getContentPane().setLayout(new GridBagLayout());\n\t\treminderTable = new JTable();\n\t\tbuttonPanel = new JPanel();\n\n\t\treminderTable.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0,\n\t\t\t\t0, 0)));\n\t\t\n\t\treminderTable.setRowSelectionAllowed(false);\n\n\t\t\n\t\tJScrollPane jScrollPane1 = new JScrollPane();\n\t\tjScrollPane1.setPreferredSize(new java.awt.Dimension(554, 404));\n\t\tjScrollPane1.setViewportView(reminderTable);\n\n\t\tJButton hideButton = new JButton();\n\t\t\n\t\t//hideButton.setIcon(IconHelper.getIcon(\n\t\t//\t\t\"/resource/Delete16.gif\")));\n\t\thideButton.setText(Resource.getResourceString(\"Hide\"));\n\n\t\thideButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfor( ReminderInstance inst : getSelectedReminders())\n\t\t\t\t{\n\t\t\t\t\tinst.setHidden(true);\n\t\t\t\t}\n\t\t\t\trefresh(true);\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(hideButton);\n\n\t\tJButton doneButton = new JButton();\n\n\t\tdoneButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Undo16.gif\"));\n\t\tResourceHelper.setText(doneButton, \"Done_(Delete)\");\n\t\tdoneButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfor( ReminderInstance inst : getSelectedReminders())\n\t\t\t\t{\n\t\t\t\t\tif( inst.isTodo())\n\t\t\t\t\t{\n\t\t\t\t\t\tinst.do_todo(true);\n\t\t\t\t\t}\n\t\t\t\t}\t\t\t\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(doneButton);\n\n\t\tJButton donendButton = new JButton();\n\n\t\tdonendButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Undo16.gif\"));\n\t\tResourceHelper.setText(donendButton, \"Done_(No_Delete)\");\n\t\tdonendButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tfor( ReminderInstance inst : getSelectedReminders())\n\t\t\t\t{\n\t\t\t\t\tif( inst.isTodo())\n\t\t\t\t\t{\n\t\t\t\t\t\tinst.do_todo(false);\n\t\t\t\t\t}\n\t\t\t\t}\t\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(donendButton);\n\n\t\tJButton resetButton = new JButton();\n\t\tresetButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Undo16.gif\"));\n\t\tResourceHelper.setText(resetButton, \"Unhide_All\");\n\t\tresetButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tReminderListManager m = (ReminderListManager) ReminderListManager\n\t\t\t\t\t\t\t.getReference();\n\t\t\t\t\tList<ReminderInstance> list = m.getReminders();\n\t\t\t\t\tfor (ReminderInstance inst : list)\n\t\t\t\t\t\tinst.setHidden(false);\n\t\t\t\t\trefresh(true);\n\t\t\t\t\tReminderManager.getReminderManager().checkModelsForReminders();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(resetButton);\n\n\t\tquietBox.setText(Resource.getResourceString(\"Quiet\"));\n\n\t\tbuttonPanel.add(quietBox);\n\n\t\tthis.getContentPane().add(\n\t\t\t\tjScrollPane1,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH,\n\t\t\t\t\t\t1.0, 1.0));\n\n\t\tthis.getContentPane().add(buttonPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 2));\n\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\t\trefresh(false);\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * reload the UI from the reminders held by the ReminderListManager\n\t */\n\tpublic void refresh(boolean silent) {\n\n\t\t// get the list of reminders managed by the ReminderListManager\n\t\tReminderListManager m = (ReminderListManager) ReminderListManager\n\t\t\t\t.getReference();\n\t\tList<ReminderInstance> list = m.getReminders();\n\n\t\t// table will be sorted by the hidden column - minutes to go\n\t\t// table will not be user sortable\n\t\tTableSorter tm = (TableSorter) reminderTable.getModel();\n\t\ttm.sortByColumn(ReminderList.TOGO_MINUTES_COLUMN);\n\t\ttm.setRowCount(0);\n\n\t\t// clear selection\n\t\treminderTable.clearSelection();\n\t\t\n\t\tArrayList<String> notifiers = new ArrayList<String>();\n\n\t\t// add all reminders\n\t\tfor (ReminderInstance inst : list) {\n\n\t\t\t// skip hidden reminders. These have been previously hidden by the\n\t\t\t// user\n\t\t\t// and won't come back unless the user resets them\n\t\t\tif (inst.isHidden())\n\t\t\t\tcontinue;\n\n\t\t\tString message = inst.calculateToGoMessage();\n\t\t\tif (message == null)\n\t\t\t\tcontinue;\n\n\t\t\t// set the shown flag for this reminder - meaning that it was shown\n\t\t\t// for any of its times\n\t\t\tinst.setShown(true);\n\t\t\t\n\t\t\tString tx = DateFormat.getDateInstance(DateFormat.SHORT).format(\n\t\t\t\t\tinst.getInstanceTime());\n\t\t\ttx += \" \" + inst.getText();\n\t\t\tnotifiers.add(tx);\n\t\t\t\n\t\t\t// build a table row\n\t\t\tObject[] row = new Object[5];\n\n\t\t\t\n\t\t\trow[ReminderList.SELECT_COLUMN] = Boolean.FALSE; \n\t\t\t\n\t\t\trow[ReminderList.TEXT_COLUMN] = inst;\n\n\t\t\trow[ReminderList.TOGO_MESSAGE_COLUMN] = message;\n\n\t\t\trow[ReminderList.REMINDER_INSTANCE_COLUMN] = inst;\n\n\t\t\tif (inst.isNote() && inst.isTodo())\n\t\t\t\trow[ReminderList.TOGO_MINUTES_COLUMN] = Long.valueOf(inst.getInstanceTime().getTime()); // sort todos last\n\t\t\telse\n\t\t\t\trow[ReminderList.TOGO_MINUTES_COLUMN] = Long.valueOf(inst.minutesToGo());\n\t\t\t\n\t\t\t\n\n\t\t\ttm.addRow(row);\n\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t\n//\t\t\tif( !silent && Prefs.getBoolPref(PrefName.TASKBAR_REMINDERS)) {\n//\t\t\t\tfor( String s : notifiers)\n//\t\t\t\t\tDorkTrayIconProxy.displayNotification(\"Borg \" + Resource.getResourceString(\"Reminder\"), s);\n//\t\t\t}\n\n\t\t}\n\n\t\tif (!silent && !quietBox.isSelected()) {\n\t\t\tthis.setVisible(true);\n\t\t\tthis.toFront();\n\t\t\tReminderSound.playReminderSound(Prefs\n\t\t\t\t\t.getPref(PrefName.BEEPINGREMINDERS));\n\t\t}\n\t\t\n\t\t\n\t}\n\n\t/**\n\t * just update the reminder times\n\t */\n\tpublic void updateTimes() {\n\n\t\tint selected = reminderTable.getSelectedRow();\n\n\t\tTableSorter tm = (TableSorter) reminderTable.getModel();\n\n\t\tfor (int index = 0; index < tm.getRowCount(); index++) {\n\t\t\tObject o = tm.getValueAt(index, ReminderList.REMINDER_INSTANCE_COLUMN);\n\t\t\tif (o != null && o instanceof ReminderInstance) {\n\n\t\t\t\tReminderInstance inst = (ReminderInstance) o;\n\t\t\t\tString message = inst.calculateToGoMessage();\n\t\t\t\tif (message != null) {\n\t\t\t\t\ttm.setValueAt(message, index, ReminderList.TOGO_MESSAGE_COLUMN);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (selected != -1)\n\t\t\treminderTable.getSelectionModel().setSelectionInterval(selected, selected);\n\n\t}\n\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderListManager.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n/*\n * popups.java\n *\n * Created on January 16, 2004, 3:08 PM\n */\n\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.model.Model.ChangeEvent;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * A Reminder List Manager. This class manages a list of reminder instances and\n * updates that list periodically to hold reminders that are being show or that\n * have been shown and have been hidden by the user. It also reacts to model\n * change events if appointments are deleted. It also manages a ReminderList UI\n * object to show the reminders\n */\npublic class ReminderListManager extends ReminderManager {\n\n\t/**\n\t * Gets the singleton.\n\t * \n\t * @return the singleton\n\t */\n\tpublic static ReminderManager getReference() {\n\t\tif (singleton == null) {\n\t\t\tsingleton = new ReminderListManager();\n\t\t}\n\t\treturn singleton;\n\t}\n\n\t// the UI\n\tprivate final ReminderList reminderList = new ReminderList();\n\n\t// managed reminder instances\n\tprivate final List<ReminderInstance> reminders = new ArrayList<ReminderInstance>();\n\n\t/**\n\t * constructor\n\t */\n\tprivate ReminderListManager() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tpublic void addToUI(ReminderInstance instance) {\n\n\t\t// skip appt if it is already in the reminders list\n\t\t// this means that it is already showing - or was shown\n\t\t// and hidden already\n\t\tif (reminders.contains(instance))\n\t\t\treturn;\n\n\t\treminders.add(instance);\n\n\t\t// do not refresh UI - that will happen during periodicUpdate()\n\t}\n\n\t/**\n\t * get the list of reminder instances\n\t * \n\t * @return the reminder instances\n\t */\n\tpublic List<ReminderInstance> getReminders() {\n\t\treturn reminders;\n\t}\n\n\t/**\n\t * Hide the reminder list window\n\t */\n\t@Override\n\tpublic void hideAll() {\n\t\treminderList.setVisible(false);\n\t}\n\n\t@Override\n\t/**\n\t * check the list of reminders to see if any are changing. this would be due to a reminder \n\t * reaching a new user-tunable reminder time or an untimed todo getting its half-hour update\n\t */\n\tpublic void periodicUpdate() {\n\n\t\tboolean needUpdate = false;\n\n\t\t// list of keys to reminders that no longer need to be shown\n\t\tArrayList<ReminderInstance> deletedReminders = new ArrayList<ReminderInstance>();\n\n\t\t// iterate through existing popups\n\t\tfor (ReminderInstance reminderInstance : reminders) {\n\n\t\t\tif (!reminderInstance.shouldBeShown()) {\n\t\t\t\t// dispose of popup and add to delete list\n\t\t\t\tdeletedReminders.add(reminderInstance);\n\t\t\t\tneedUpdate = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// skip hidden reminders\n\t\t\tif (reminderInstance.isHidden())\n\t\t\t\tcontinue;\n\n\t\t\t// untimed todo\n\t\t\tif (reminderInstance.isNote() && reminderInstance.isTodo()) {\n\n\t\t\t\tif (!reminderInstance.isShown()\n\t\t\t\t\t\t|| shouldShowUntimedTodosNow()) {\n\t\t\t\t\tneedUpdate = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t} else if (reminderInstance.dueForPopup() != -1) {\n\t\t\t\tneedUpdate = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t}\n\n\t\t// delete the popup map entries for reminders that we disposed of\n\t\tfor (ReminderInstance inst : deletedReminders) {\n\t\t\treminders.remove(inst);\n\t\t}\n\n\t\tif (needUpdate)\n\t\t\treminderList.refresh();\n\t\telse if (reminderList.isShowing()) {\n\t\t\treminderList.updateTimes();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.Model.Listener#refresh()\n\t */\n\t@Override\n\tpublic void refresh() {\n\n\t\t// list of keys to reminders that no longer need to be shown\n\t\tArrayList<ReminderInstance> deletedReminders = new ArrayList<ReminderInstance>();\n\n\t\t// loop through the existing reminders\n\t\tfor (ReminderInstance reminderInstance : reminders) {\n\n\t\t\t// check if db has changed\n\t\t\tif (reminderInstance.reloadAndCheckForChanges())\n\t\t\t\tdeletedReminders.add(reminderInstance);\n\n\t\t\telse if (!reminderInstance.shouldBeShown()) {\n\t\t\t\t// dispose of popup and add to delete list\n\t\t\t\tdeletedReminders.add(reminderInstance);\n\t\t\t}\n\n\t\t}\n\n\t\t// delete the popup map entries for reminders that we disposed of\n\t\tfor (ReminderInstance inst : deletedReminders) {\n\t\t\treminders.remove(inst);\n\t\t}\n\n\t\t// refresh the list, but don't show unless it is already showing\n\t\tif (!deletedReminders.isEmpty())\n\t\t\treminderList.refresh(!reminderList.isShowing());\n\n\t}\n\n\t/**\n\t * stop the timer and remove all popups\n\t */\n\t@Override\n\tpublic void remove() {\n\t\tsuper.remove();\n\n\t\treminderList.destroy();\n\t}\n\n\t/**\n\t * show the list\n\t */\n\t@Override\n\tpublic void showAll() {\n\t\treminderList.setVisible(true);\n\t\treminderList.toFront();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderManager.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n/*\n * popups.java\n *\n * Created on January 16, 2004, 3:08 PM\n */\n\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.*;\nimport net.sf.borg.model.entity.*;\n\nimport javax.swing.*;\nimport java.util.Timer;\nimport java.util.*;\n\n/**\n * Abstract Base class for managing reminders. A reminder manager controls the\n * UI that shows reminder instances. It also manages a list of reminders (a\n * model for the UI) and must check and update the model as time passes. It must\n * also react to appointment model changes.\n */\npublic abstract class ReminderManager implements Model.Listener, Prefs.Listener {\n\n\t/** The singleton. */\n\tstatic protected ReminderManager singleton = null;\n\n\tpublic static ReminderManager getReminderManager() {\n\t\treturn singleton;\n\t}\n\n\t/** runnable task that checks if we need to pop up any popups */\n\tfinal Runnable doModelCheck = new Runnable() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tcheckModelsForReminders();\n\t\t}\n\t};\n\n\t/**\n\t * The timer that periodically checks to see if we need to popup a new\n\t * reminder\n\t */\n\tprivate Timer timer = null;\n\n\t/**\n\t * constructor\n\t */\n\tprotected ReminderManager() {\n\n\t\t// listen for appointment model changes\n\t\tAppointmentModel.getReference().addListener(this);\n\t\tTaskModel.getReference().addListener(this);\n\t\tAddressModel.getReference().addListener(this);\n\n\t\tPrefs.addListener(this);\n\n\t\t// start the timer\n\t\t// for consistency - it will start at the beginning of the next minute\n\t\t// the interval between runs is a user preference\n\t\ttimer = new Timer(\"ReminderTimer\");\n\t\tGregorianCalendar cur = new GregorianCalendar();\n\t\tint secs_left = 60 - cur.get(Calendar.SECOND);\n\t\ttimer.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tSwingUtilities.invokeLater(doModelCheck);\n\t\t\t}\n\t\t}, secs_left * 1000, 1 * 60 * 1000);\n\n\t}\n\n\t@Override\n\tpublic void prefsChanged() {\n\t\trefresh();\n\t}\n\n\t/**\n\t * add a reminder to the UI\n\t *\n\t * @param instance\n\t *            the reminder instance\n\t */\n\tpublic abstract void addToUI(ReminderInstance instance);\n\n\t/**\n\t * check the models to see if any new reminder messages needed and show\n\t * them. Also update any messages that are already being shown\n\t */\n\tpublic void checkModelsForReminders() {\n\n\t\t// do nothing if the reminder feature is off\n\t\t// this check is inside the timer logic so that\n\t\t// the user can turn on the feature and not have to restart\n\t\t// to get popups\n\t\tString enable = Prefs.getPref(PrefName.REMINDERS);\n\t\tif (enable.equals(\"false\"))\n\t\t\treturn;\n\n\t\tgetAppointmentReminders();\n\n\t\tgetBirthdayReminders();\n\n\t\tgetTaskReminders();\n\n\t\t// update the existing reminders in the model and UI as needed due to\n\t\t// passage of time\n\t\tperiodicUpdate();\n\n\t}\n\n\tprivate void getTaskReminders() {\n\t\tif (Prefs.getBoolPref(PrefName.TASKREMINDERS)) {\n\n\t\t\ttry {\n\t\t\t\tCollection<Project> pjs = TaskModel.getReference()\n\t\t\t\t\t\t.getProjects();\n\t\t\t\tfor (Project pj : pjs) {\n\t\t\t\t\tif (pj.getDueDate() == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// skip closed projects\n\t\t\t\t\tif (pj.getStatus().equals(\n\t\t\t\t\t\t\tResource.getResourceString(\"CLOSED\")))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t// filter by category\n\t\t\t\t\tif (!CategoryModel.getReference().isShown(pj.getCategory()))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tReminderInstance inst = new ProjectReminderInstance(pj);\n\n\t\t\t\t\tif (!inst.shouldBeShown())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\taddToUI(inst);\n\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t\t// get all tasks\n\t\t\tVector<Task> mrs = TaskModel.getReference().get_tasks();\n\t\t\tfor (int i = 0; i < mrs.size(); i++) {\n\n\t\t\t\tTask mr = mrs.elementAt(i);\n\t\t\t\tif (mr.getDueDate() == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tReminderInstance inst = new TaskReminderInstance(mr);\n\n\t\t\t\tif (!inst.shouldBeShown())\n\t\t\t\t\tcontinue;\n\n\t\t\t\taddToUI(inst);\n\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tCollection<Subtask> sts = TaskModel.getReference()\n\t\t\t\t\t\t.getSubTasks();\n\t\t\t\tfor (Subtask st : sts) {\n\t\t\t\t\tif (st.getDueDate() == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (st.getCloseDate() != null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tTask task = TaskModel.getReference().getTask(\n\t\t\t\t\t\t\tst.getTask().intValue());\n\t\t\t\t\tString cat = task.getCategory();\n\n\t\t\t\t\tif (!CategoryModel.getReference().isShown(cat))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tReminderInstance inst = new SubtaskReminderInstance(st);\n\n\t\t\t\t\tif (!inst.shouldBeShown())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\taddToUI(inst);\n\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void getAppointmentReminders() {\n\n\t\t// determine most future day that we have to consider\n\t\tint earliestReminderTime = -100000;\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); i++) {\n\t\t\tint time = ReminderTimes.getTimes(i);\n\t\t\tif (time > earliestReminderTime) {\n\t\t\t\tearliestReminderTime = ReminderTimes.getTimes(i);\n\t\t\t}\n\t\t}\n\n\t\t// determine how many days ahead we have to fetch appointments from\n\t\tint daysAhead = earliestReminderTime / (24 * 60) + 1;\n\t\tif (daysAhead < 1)\n\t\t\tdaysAhead = 1;\n\n\t\t// process the appointments from each day\n\t\t// it does not matter if we find appointments that are a few hours\n\t\t// too far in the future to be considered - they will be filtered later\n\t\tCalendar cal = new GregorianCalendar();\n\t\tfor (int dayAhead = 0; dayAhead < daysAhead; dayAhead++) {\n\t\t\tList<Integer> apptKeyList = AppointmentModel.getReference()\n\t\t\t\t\t.getAppts(cal.getTime());\n\n\t\t\tif (apptKeyList != null) {\n\n\t\t\t\tAppointment appt;\n\n\t\t\t\t// iterate through the appts\n\t\t\t\tfor (Integer apptKey : apptKeyList) {\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// read the appt record from the data model\n\t\t\t\t\t\tappt = AppointmentModel.getReference().getAppt(\n\t\t\t\t\t\t\t\tapptKey.intValue());\n\n\t\t\t\t\t\t// skip untimed todos here - will be collected later on\n\t\t\t\t\t\tif (AppointmentModel.isNote(appt) && appt.isTodo())\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t// calculate instance time\n\t\t\t\t\t\tCalendar instTime = new GregorianCalendar();\n\t\t\t\t\t\tinstTime.setTime(appt.getDate());\n\t\t\t\t\t\tinstTime.set(Calendar.SECOND, 0);\n\t\t\t\t\t\tinstTime.set(Calendar.MILLISECOND, 0);\n\t\t\t\t\t\tinstTime.set(Calendar.YEAR, cal.get(Calendar.YEAR));\n\t\t\t\t\t\tinstTime.set(Calendar.MONTH, cal.get(Calendar.MONTH));\n\t\t\t\t\t\tinstTime.set(Calendar.DATE, cal.get(Calendar.DATE));\n\n\t\t\t\t\t\tReminderInstance apptInstance = new ApptReminderInstance(\n\t\t\t\t\t\t\t\tappt, instTime.getTime());\n\n\t\t\t\t\t\t// if the appointment doesn't qualify for a popup\n\t\t\t\t\t\t// then don't show one\n\t\t\t\t\t\tif (!apptInstance.shouldBeShown())\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\taddToUI(apptInstance);\n\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// go to next day\n\t\t\tcal.add(Calendar.DATE, 1);\n\t\t}\n\n\t\t// get todos that are not done\n\t\tCollection<Appointment> tds = AppointmentModel.getReference()\n\t\t\t\t.get_todos();\n\t\tif (tds != null) {\n\t\t\tIterator<Appointment> it = tds.iterator();\n\n\t\t\twhile (it.hasNext()) {\n\n\t\t\t\tAppointment appt = it.next();\n\n\t\t\t\t// skip timed todos here - done above\n\t\t\t\tif (!AppointmentModel.isNote(appt))\n\t\t\t\t\tcontinue;\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// instance date is date of next todo\n\t\t\t\t\tDate nt = appt.getNextTodo();\n\t\t\t\t\tif (nt == null)\n\t\t\t\t\t\tnt = appt.getDate();\n\n\t\t\t\t\tReminderInstance apptInstance = new ApptReminderInstance(\n\t\t\t\t\t\t\tappt, nt);\n\n\t\t\t\t\tif (!apptInstance.shouldBeShown())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\taddToUI(apptInstance);\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void getBirthdayReminders() {\n\n\t\t// birthdays\n\t\tint bd_days = Prefs.getIntPref(PrefName.BIRTHDAYREMINDERDAYS);\n\t\tif (bd_days >= 0) {\n\t\t\tDate now = new Date();\n\t\t\tGregorianCalendar cal = new GregorianCalendar();\n\t\t\tcal.setTime(now);\n\t\t\tcal.set(Calendar.SECOND, 0);\n\t\t\tcal.set(Calendar.MINUTE, 0);\n\t\t\tcal.set(Calendar.HOUR_OF_DAY, 0);\n\t\t\tCollection<Address> addrs;\n\t\t\ttry {\n\t\t\t\taddrs = AddressModel.getReference().getAddresses();\n\t\t\t\tif (addrs != null) {\n\t\t\t\t\tfor (Address addr : addrs) {\n\n\t\t\t\t\t\tDate bd = addr.getBirthday();\n\t\t\t\t\t\tif (bd == null)\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t// set time to end of the day. this will allow reminders\n\t\t\t\t\t\t// to pop up all day on the\n\t\t\t\t\t\t// birthday itself\n\t\t\t\t\t\tCalendar bdcal = new GregorianCalendar();\n\t\t\t\t\t\tbdcal.setTime(bd);\n\t\t\t\t\t\tbdcal.set(Calendar.YEAR, cal.get(Calendar.YEAR));\n\t\t\t\t\t\tbdcal.set(Calendar.SECOND, 59);\n\t\t\t\t\t\tbdcal.set(Calendar.MINUTE, 59);\n\t\t\t\t\t\tbdcal.set(Calendar.HOUR_OF_DAY, 23);\n\n\t\t\t\t\t\t// if the birthday is passed for this year, try next\n\t\t\t\t\t\t// year (in case we are close enough\n\t\t\t\t\t\t// to January birthdays for next year)\n\t\t\t\t\t\tif (bdcal.before(cal)) {\n\t\t\t\t\t\t\tbdcal.add(Calendar.YEAR, 1);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tReminderInstance inst = new BirthdayReminderInstance(\n\t\t\t\t\t\t\t\taddr, bdcal.getTime());\n\n\t\t\t\t\t\tif (!inst.shouldBeShown())\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\taddToUI(inst);\n\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Hide all reminders\n\t */\n\tpublic abstract void hideAll();\n\n\t/**\n\t * update any visible reminders as needed - i.e. to catch changes due to the\n\t * passage of time. This is NOT called for model changes.\n\t */\n\tpublic abstract void periodicUpdate();\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see net.sf.borg.model.Model.Listener#refresh()\n\t */\n\t/**\n\t * refresh the popup states depending on the state of the models.\n\t * In particular, clean up any reminders that should no longer be shown because\n\t * of model changes.\n\t */\n\tabstract public void refresh();\n\n\t/**\n\t * stop the timer\n\t */\n\tpublic void remove() {\n\t\ttimer.cancel();\n\n\t\tsingleton = null;\n\t}\n\n\t/**\n\t * show all reminders\n\t */\n\tpublic abstract void showAll();\n\n\t/**\n\t * determine if we should show a reminder for untimed todos during this\n\t * periodic update\n\t *\n\t * @return true if we should show the reminder\n\t */\n\tprotected boolean shouldShowUntimedTodosNow() {\n\n\t\tGregorianCalendar now = new GregorianCalendar();\n\t\tint todoFreq = Prefs.getIntPref(PrefName.TODOREMINDERMINS);\n\n\t\t// *** show untimed todos periodically and on startup\n\t\t// if no todo reminders are wanted, then skip this\n\t\tif (todoFreq == 0)\n\t\t\treturn false;\n\n\t\t// determine minutes since midnight\n\t\tint hr = now.get(Calendar.HOUR_OF_DAY);\n\t\tint min = new GregorianCalendar().get(Calendar.MINUTE);\n\t\tint mins_since_midnight = hr * 60 + min;\n\n\t\treturn (mins_since_midnight % todoFreq == 0);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderPopup.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.popup;\n\nimport java.awt.Color;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.text.DateFormat;\nimport java.util.Date;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.WindowConstants;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.model.Theme;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.View;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\n/**\n * Popop window for a single Reminder\n */\nclass ReminderPopup extends View {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The Reminder Instance being shown. */\n\tprivate ReminderInstance reminderInstance = null;\n\n\t/** The appointment information. */\n\tprivate JLabel appointmentInformation = null;\n\n\t/** The time to go message. */\n\tprivate JLabel timeToGoMessage = null;\n\n\t/** The no more reminders button. */\n\tprivate JRadioButton noMoreRemindersButton = null;\n\n\t/**\n\t * Instantiates a new reminder popup for an appointment\n\t * \n\t * @param inst\n\t *            the reminder instance to show\n\t */\n\tpublic ReminderPopup(ReminderInstance inst) {\n\n\t\tsuper();\n\n\t\treminderInstance = inst;\n\n\t\tthis.setTitle(\"Borg \" + Resource.getResourceString(\"Reminder\"));\n\n\t\tinitialize();\n\n\t\t// set appt info in the reminder\n\t\tDateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);\n\t\tString apptinfoText = df.format(inst.getInstanceTime());\n\t\t\n\t\tapptinfoText += \" \" + reminderInstance.getText();\n\t\tappointmentInformation.setText(apptinfoText);\n\n\t\tthis.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.View#destroy()\n\t */\n\t@Override\n\tpublic void destroy() {\n\t\tthis.dispose();\n\t}\n\n\t/**\n\t * This method initializes the UI\n\t * \n\t */\n\tprivate void initialize() {\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\t\ttopPanel.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\tappointmentInformation = new JLabel();\n\t\tappointmentInformation\n\t\t\t\t.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);\n\t\ttopPanel.add(appointmentInformation, GridBagConstraintsFactory.create(\n\t\t\t\t0, 0, GridBagConstraints.NONE, 1.0, 0.0));\n\n\t\ttimeToGoMessage = new JLabel();\n\t\ttimeToGoMessage.setText(\"\");\n\t\ttimeToGoMessage\n\t\t\t\t.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);\n\n\t\ttopPanel.add(timeToGoMessage, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.NONE, 1.0, 0.0));\n\t\tnoMoreRemindersButton = new JRadioButton();\n\t\tResourceHelper.setText(noMoreRemindersButton, \"No_more\");\n\t\tnoMoreRemindersButton\n\t\t\t\t.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);\n\t\tnoMoreRemindersButton.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\ttopPanel.add(noMoreRemindersButton, GridBagConstraintsFactory.create(0,\n\t\t\t\t2, GridBagConstraints.NONE, 1.0, 0.0));\n\n\t\t//\n\t\t// button panel\n\t\t//\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\tbuttonPanel.setLayout(new GridBagLayout());\n\n\t\tJButton dismissButton = new JButton();\n\t\tResourceHelper.setText(dismissButton, \"Dismiss\");\n\t\tdismissButton.setActionCommand(\"close_it\");\n\t\tdismissButton.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\tdismissButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tif (noMoreRemindersButton.isSelected()) {\n\t\t\t\t\treminderInstance.setHidden(true);\n\t\t\t\t\tdispose();\n\t\t\t\t} else {\n\t\t\t\t\tsetVisible(false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(dismissButton, GridBagConstraintsFactory.create(0, 0));\n\n\t\tif (reminderInstance.isTodo() ) {\n\n\t\t\tJButton doneButton = new JButton();\n\t\t\tdoneButton.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\t\tResourceHelper.setText(doneButton, \"Done_(Delete)\");\n\t\t\tdoneButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\treminderInstance.do_todo(true);\n\t\t\t\t\tdestroy();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(doneButton, GridBagConstraintsFactory.create(1, 0));\n\n\t\t\tJButton doneNoDeleteButton = new JButton();\n\t\t\tdoneNoDeleteButton.setBackground(new Color(Theme.getCurrentTheme().getReminderBg()));\n\n\t\t\tResourceHelper.setText(doneNoDeleteButton, \"Done_(No_Delete)\");\n\t\t\tdoneNoDeleteButton\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\treminderInstance.do_todo(false);\n\t\t\t\t\t\t\tdestroy();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\tbuttonPanel.add(doneNoDeleteButton, GridBagConstraintsFactory\n\t\t\t\t\t.create(2, 0));\n\t\t}\n\n\t\ttopPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 3));\n\n\t\tthis.setContentPane(topPanel);\n\t\tthis.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));\n\t\tthis.setSize(400, 250);\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.View#refresh()\n\t */\n\t@Override\n\tpublic void refresh() {\n\t\t// empty\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\tpublic ReminderInstance getReminderInstance() {\n\t\treturn reminderInstance;\n\t}\n\n\t/**\n\t * update the popup's message\n\t */\n\tpublic void updateMessage() {\n\t\t// read the appt and get the date\n\n\t\tString message;\n\n\t\t// untimed todo\n\t\tif (reminderInstance.isNote() && reminderInstance.isTodo()) {\n\t\t\tmessage = reminderInstance.calculateToGoMessage();\n\t\t} else {\n\t\t\t// timed appt\n\t\t\tDate d = reminderInstance.getInstanceTime();\n\t\t\tif (d == null)\n\t\t\t\treturn;\n\n\t\t\t// if alarm is due to be shown, show it and play sound\n\n\t\t\tint reminderIndex = reminderInstance.dueForPopup();\n\t\t\tif( reminderIndex == -1 )\n\t\t\t\treturn;\n\t\t\t\n\t\t\tint minutesToGo = ReminderTimes.getTimes(reminderIndex);\n\t\t\t\n\t\t\t// create a message saying how much time to go there is\n\t\t\tif (minutesToGo < 0) {\n\t\t\t\tmessage = -minutesToGo + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"minutes_ago\");\n\t\t\t} else if (minutesToGo == 0) {\n\t\t\t\tmessage = Resource.getResourceString(\"Now\");\n\t\t\t} else {\n\t\t\t\tmessage = minutesToGo + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"minute_reminder\");\n\t\t\t}\n\t\t\t\n\t\t\tgetReminderInstance().markAsShown(reminderIndex);\n\n\t\t}\n\t\t\n\t\ttimeToGoMessage.setText(message);\n\t\t\n\t\tsetVisible(true);\n\t\ttoFront();\n\t\tsetVisible(true);\n\t\t\n\t\tgetReminderInstance().setShown(true);\n\t\t\n\t\t// play a sound\n\t\tReminderSound.playReminderSound(Prefs\n\t\t\t\t.getPref(PrefName.BEEPINGREMINDERS));\n\t\t\n//\t\tif (Prefs.getBoolPref(PrefName.TASKBAR_REMINDERS)) {\n//\t\t\tString tx = DateFormat.getDateInstance(DateFormat.SHORT).format(reminderInstance.getInstanceTime());\n//\t\t\ttx += \" \" + reminderInstance.getText();\n//\t\t\tDorkTrayIconProxy.displayNotification(\"Borg \" + Resource.getResourceString(\"Reminder\"),tx);\n//\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderPopupManager.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n/*\n * popups.java\n *\n * Created on January 16, 2004, 3:08 PM\n */\n\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.model.Model.ChangeEvent;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\n/**\n * Manages the lifecycle of Reminder Popup windows.\n */\npublic class ReminderPopupManager extends ReminderManager {\n\n\t// map that maps each known reminder instance to the associated popup window\n\t// windows\n\tprivate final HashMap<ReminderInstance, ReminderPopup> pops = new HashMap<ReminderInstance, ReminderPopup>();\n\n\t/**\n\t * Gets the singleton.\n\t * \n\t * @return the singleton\n\t */\n\tpublic static ReminderManager getReference() {\n\t\tif (singleton == null) {\n\t\t\tsingleton = new ReminderPopupManager();\n\t\t}\n\t\treturn singleton;\n\t}\n\n\t/**\n\t * constructor\n\t */\n\tprivate ReminderPopupManager() {\n\t\tsuper();\n\t}\n\n\t/**\n\t * stop the timer and remove all popups\n\t */\n\t@Override\n\tpublic void remove() {\n\t\tsuper.remove();\n\n\t\tSet<Entry<ReminderInstance, ReminderPopup>> entrySet = pops.entrySet();\n\t\tfor (Entry<ReminderInstance, ReminderPopup> popupMapEntry : entrySet) {\n\n\t\t\tReminderPopup popup = popupMapEntry.getValue();\n\t\t\tif (popup == null || !popup.isDisplayable())\n\t\t\t\tcontinue;\n\n\t\t\tpopup.dispose();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.model.Model.Listener#refresh()\n\t */\n\t@Override\n\tpublic void refresh() {\n\n\t\t// list of keys to appointments that no longer need popups\n\t\tArrayList<ReminderInstance> deletedPopupKeys = new ArrayList<ReminderInstance>();\n\n\t\t// set of popup map entries\n\t\tSet<Entry<ReminderInstance, ReminderPopup>> entrySet = pops.entrySet();\n\n\t\t// loop through the existing popups\n\t\tfor (Entry<ReminderInstance, ReminderPopup> mapEntry : entrySet) {\n\n\t\t\tReminderInstance apptInstance = mapEntry.getKey();\n\n\t\t\t// get the popup window\n\t\t\tReminderPopup popupWindow = mapEntry.getValue();\n\n\t\t\t// if frame is gone (killed already), then skip it\n\t\t\tif (popupWindow == null)\n\t\t\t\tcontinue;\n\n\t\t\t// skip if popup has been disposed - but still in map\n\t\t\tif (!popupWindow.isDisplayable()) {\n\t\t\t\t// remove from map\n\t\t\t\t// map should be last reference to the frame so garbage\n\t\t\t\t// collection should now be free to clean it up\n\t\t\t\tmapEntry.setValue(null);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//if (apptInstance.isHidden())\n\t\t\t//\tcontinue;\n\n\t\t\tif (apptInstance.reloadAndCheckForChanges()) {\n\t\t\t\tpopupWindow.dispose();\n\t\t\t\tdeletedPopupKeys.add(apptInstance);\n\t\t\t}\n\n\t\t\tif (!apptInstance.shouldBeShown()) {\n\t\t\t\t// dispose of popup and add to delete list\n\t\t\t\tpopupWindow.dispose();\n\t\t\t\tdeletedPopupKeys.add(apptInstance);\n\t\t\t}\n\n\t\t}\n\n\t\t// delete the popup map entries for popups that we disposed of\n\t\tfor (ReminderInstance inst : deletedPopupKeys) {\n\t\t\tpops.remove(inst);\n\t\t}\n\n\t}\n\n\t/**\n\t * show all popups in the list\n\t */\n\t@Override\n\tpublic void showAll() {\n\t\tSet<Entry<ReminderInstance, ReminderPopup>> entrySet = pops.entrySet();\n\t\tfor (Entry<ReminderInstance, ReminderPopup> popupMapEntry : entrySet) {\n\n\t\t\tReminderPopup popup = popupMapEntry.getValue();\n\n\t\t\t// if frame is gone (killed already), then skip it\n\t\t\tif (popup == null)\n\t\t\t\tcontinue;\n\n\t\t\t// skip if popup is disposed - but still in map\n\t\t\tif (!popup.isDisplayable()) {\n\t\t\t\t// remove from map\n\t\t\t\t// map should be last reference to the frame so garbage\n\t\t\t\t// collection should now be free to clean it up\n\t\t\t\tpopupMapEntry.setValue(null);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// pop it up\n\t\t\tpopup.setVisible(true);\n\t\t\tpopup.toFront();\n\n\t\t}\n\t}\n\n\t/**\n\t * Hide all popup windows\n\t */\n\t@Override\n\tpublic void hideAll() {\n\t\tSet<Entry<ReminderInstance, ReminderPopup>> entrySet = pops.entrySet();\n\t\tfor (Entry<ReminderInstance, ReminderPopup> popupMapEntry : entrySet) {\n\n\t\t\tReminderPopup popup = popupMapEntry.getValue();\n\n\t\t\t// if frame is gone (killed already), then skip it\n\t\t\tif (popup == null)\n\t\t\t\tcontinue;\n\n\t\t\t// skip if popup is disposed - but still in map\n\t\t\tif (!popup.isDisplayable()) {\n\t\t\t\t// remove from map\n\t\t\t\t// map should be last reference to the frame so garbage\n\t\t\t\t// collection should now be free to clean it up\n\t\t\t\tpopupMapEntry.setValue(null);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// hide\n\t\t\tpopup.setVisible(false);\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addToUI(ReminderInstance instance) {\n\n\t\t// skip appt if it is already in the pops list\n\t\t// this means that it is already showing - or was shown\n\t\t// and killed already\n\t\tif (pops.containsKey(instance))\n\t\t\treturn;\n\n\t\t// create a new popup and add it to the\n\t\t// popup map along with the appt key\n\t\tReminderPopup popup = new ReminderPopup(instance);\n\t\tpops.put(instance, popup);\n\n\t\tpopup.updateMessage();\n\n\t}\n\n\t@Override\n\tpublic void periodicUpdate() {\n\t\t// if any popups that are already displayed are due for showing - make a\n\t\t// sound and raise the popup\n\n\t\t// iterate through existing popups\n\t\tSet<Entry<ReminderInstance, ReminderPopup>> entrySet = pops.entrySet();\n\t\tfor (Entry<ReminderInstance, ReminderPopup> popupMapEntry : entrySet) {\n\n\t\t\tReminderInstance instance = popupMapEntry.getKey();\n\t\t\tReminderPopup popup = popupMapEntry.getValue();\n\n\t\t\t// if popup is gone (killed already), then skip it\n\t\t\tif (popup == null)\n\t\t\t\tcontinue;\n\n\t\t\t// skip if popup is disposed - but still in map\n\t\t\tif (!popup.isDisplayable()) {\n\t\t\t\t// remove from map\n\t\t\t\t// map should be last reference to the frame so garbage\n\t\t\t\t// collection should now be free to clean it up\n\t\t\t\tpopupMapEntry.setValue(null);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (instance.isHidden())\n\t\t\t\tcontinue;\n\n\t\t\t// untimed todo\n\t\t\tif (instance.isNote() && instance.isTodo()) {\n\n\t\t\t\tif (popup.getReminderInstance().isShown()\n\t\t\t\t\t\t&& !shouldShowUntimedTodosNow())\n\t\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\t// set the time to go message\n\t\t\tpopup.updateMessage();\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/ReminderSound.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2010 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport javax.sound.sampled.AudioSystem;\nimport javax.sound.sampled.Clip;\nimport javax.sound.sampled.LineUnavailableException;\nimport javax.sound.sampled.UnsupportedAudioFileException;\nimport java.awt.*;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\npublic class ReminderSound {\n\t/**\n\t * play the reminder sound indicated by soundOption\n\t * \n\t * @param soundOption\n\t *            - true (default sound), false (no-sound), system-beep, or a\n\t *            filename\n\t */\n\tstatic public void playReminderSound(String soundOption) {\n\t\tif (soundOption.equals(\"system-beep\")) {\n\t\t\tToolkit.getDefaultToolkit().beep();\n\t\t} else if (soundOption.equals(\"true\")) {\n\t\t\tURL snd = ReminderPopupManager.class.getResource(\"/resource/blip.wav\");\n\t\t\ttry {\n\t\t\t\tClip sound = AudioSystem.getClip();\n\t\t\t\tsound.open(AudioSystem.getAudioInputStream(snd));\n\t\t\t\tsound.start();\n\n\t\t\t} catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {\n\t\t\t\t// no error\n\t\t\t}\n\t\t\t\n\t\t} else if (!soundOption.equals(\"false\")) {\n\t\t\ttry {\n\t\t\t\tClip sound = AudioSystem.getClip();\n\t\t\t\tsound.open(AudioSystem.getAudioInputStream(new File(soundOption)));\n\t\t\t\tsound.start();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// no error\n\t\t\t}\n\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/SubtaskReminderInstance.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2011 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Subtask;\n\nimport java.util.Date;\n\n/**\n * A Reminder Instance class that holds a Task\n * \n */\npublic class SubtaskReminderInstance extends ReminderInstance {\n\n\tprivate Subtask subtask;\n\n\t@Override\n\tpublic void do_todo(boolean delete) {\n\t\ttry {\n\t\t\tsubtask = TaskModel.getReference().getSubTask(subtask.getKey());\n\t\t\tsubtask.setCloseDate(new Date());\n\t\t\tTaskModel.getReference().saveSubTask(subtask);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tSubtaskReminderInstance other = (SubtaskReminderInstance) obj;\n\t\tif (subtask == null) {\n\t\t\tif (other.subtask != null)\n\t\t\t\treturn false;\n\t\t} else if (subtask.getKey() != other.subtask.getKey()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getInstanceTime() == null) {\n\t\t\treturn other.getInstanceTime() == null;\n\t\t} else return getInstanceTime().equals(other.getInstanceTime());\n\t}\n\n\t@Override\n\tpublic int getCurrentReminder() {\n\t\treturn -1;\n\t}\n\n\t/**\n\t * @param task\n\t */\n\tpublic SubtaskReminderInstance(Subtask subtask) {\n\t\tsuper();\n\t\tthis.subtask = subtask;\n\t\tthis.setInstanceTime(subtask.getDueDate());\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn subtask.getText();\n\t}\n\n\t@Override\n\tpublic String calculateToGoMessage() {\n\t\treturn Resource.getResourceString(\"subtask\");\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((subtask == null) ? 0 : subtask.getKey());\n\t\tresult = prime\n\t\t\t\t* result\n\t\t\t\t+ ((getInstanceTime() == null) ? 0 : getInstanceTime()\n\t\t\t\t\t\t.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean isNote() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTodo() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean reloadAndCheckForChanges() {\n\t\ttry {\n\t\t\tSubtask orig = subtask;\n\t\t\tsubtask = TaskModel.getReference().getSubTask(subtask.getKey());\n\t\t\tif (subtask == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif (subtask.getCloseDate() != null)\n\t\t\t\treturn true;\n\n\n\t\t\tif (subtask.getDueDate() == null || !subtask.getDueDate()\n\t\t\t\t\t.equals(orig.getDueDate())) {\n\t\t\t\t// date changed - delete. new instance will be added on\n\t\t\t\t// periodic update\n\t\t\t\treturn true;\n\t\t\t}\n\n\n\t\t\t// delete it if the text changed - will be added back in\n\t\t\t// periodic check for\n\t\t\t// popups\n\t\t\tif (!subtask.getDescription()\n\t\t\t\t\t.equals(orig.getDescription())) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t\n\t\t} catch (Exception e) {\n\n\t\t\t// task cannot be read, must have been deleted\n\t\t\tsubtask = null;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean shouldBeShown() {\n\t\t\n\t\tif( !Prefs.getBoolPref(PrefName.TASKREMINDERS))\n\t\t\treturn false;\n\t\t\n\t\tif( subtask == null || subtask.getCloseDate() != null )\n\t\t\treturn false;\n\n\t\t// determine how far away the task is\n\t\tDate d = DateUtil.setToMidnight(getInstanceTime());\n\t\tlong minutesToGo =d.getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\n\t\tint earliestReminderTime = -100000;\n\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); i++) {\n\t\t\tint time = ReminderTimes.getTimes(i);\n\t\t\tif (time > earliestReminderTime) {\n\t\t\t\tearliestReminderTime = time;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif( earliestReminderTime == -100000)\n\t\t\treturn false;\n\n\t\treturn (minutesToGo < earliestReminderTime);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/TaskReminderInstance.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2011 by Mike Berger\n */\npackage net.sf.borg.ui.popup;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.ReminderTimes;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Task;\n\nimport java.util.Date;\n\n/**\n * A Reminder Instance class that holds a Task\n * \n */\npublic class TaskReminderInstance extends ReminderInstance {\n\n\tprivate Task task;\n\n\t@Override\n\tpublic void do_todo(boolean delete) {\n\t\ttry {\n\t\t\tTaskModel.getReference().close(task.getKey());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tTaskReminderInstance other = (TaskReminderInstance) obj;\n\t\tif (task == null) {\n\t\t\tif (other.task != null)\n\t\t\t\treturn false;\n\t\t} else if (task.getKey() != other.task.getKey()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (getInstanceTime() == null) {\n\t\t\treturn other.getInstanceTime() == null;\n\t\t} else return getInstanceTime().equals(other.getInstanceTime());\n\t}\n\n\t@Override\n\tpublic int getCurrentReminder() {\n\t\treturn -1;\n\t}\n\n\t/**\n\t * @param task\n\t */\n\tpublic TaskReminderInstance(Task task) {\n\t\tsuper();\n\t\tthis.task = task;\n\t\tthis.setInstanceTime(task.getDueDate());\n\t}\n\n\t@Override\n\tpublic String getText() {\n\t\treturn task.getText();\n\t}\n\n\t@Override\n\tpublic String calculateToGoMessage() {\n\t\treturn Resource.getResourceString(\"task\");\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((task == null) ? 0 : task.getKey());\n\t\tresult = prime\n\t\t\t\t* result\n\t\t\t\t+ ((getInstanceTime() == null) ? 0 : getInstanceTime()\n\t\t\t\t\t\t.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean isNote() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTodo() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean reloadAndCheckForChanges() {\n\t\ttry {\n\t\t\tTask orig = task;\n\t\t\ttask = TaskModel.getReference().getTask(task.getKey());\n\t\t\tif (task == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tif( TaskModel.isClosed(task))\n\t\t\t\treturn true;\n\t\t\t\n\t\t\tif( task.getDueDate() == null )\n\t\t\t\treturn true;\n\n\t\t\tif (!task.getDueDate()\n\t\t\t\t\t.equals(orig.getDueDate())) {\n\t\t\t\t// date changed - delete. new instance will be added on\n\t\t\t\t// periodic update\n\t\t\t\treturn true;\n\t\t\t}\n\n\n\t\t\t// delete it if the text changed - will be added back in\n\t\t\t// periodic check for\n\t\t\t// popups\n\t\t\tif (!task.getSummary()\n\t\t\t\t\t.equals(orig.getSummary())) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t\n\t\t} catch (Exception e) {\n\n\t\t\t// task cannot be read, must have been deleted\n\t\t\t// this is an expected case when appointments are deleted\n\t\t\ttask = null;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean shouldBeShown() {\n\t\t\n\t\tif( !Prefs.getBoolPref(PrefName.TASKREMINDERS))\n\t\t\treturn false;\n\t\t\n\t\tif( task == null || TaskModel.isClosed(task) )\n\t\t\treturn false;\n\n\t\t// determine how far away the task is\n\t\tDate d = DateUtil.setToMidnight(getInstanceTime());\n\t\tlong minutesToGo =d.getTime() / (1000 * 60)\n\t\t\t\t- new Date().getTime() / (1000 * 60);\n\t\tint earliestReminderTime = -100000;\n\n\t\tfor (int i = 0; i < ReminderTimes.getNum(); i++) {\n\t\t\tint time = ReminderTimes.getTimes(i);\n\t\t\tif (time > earliestReminderTime) {\n\t\t\t\tearliestReminderTime = time;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif( earliestReminderTime == -100000)\n\t\t\treturn false;\n\n\t\treturn (minutesToGo < earliestReminderTime);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/popup/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  Contains the classes that produce and manage the Reminder Popup Windows\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/ProjectPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.task;\n\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.util.Collection;\nimport java.util.Date;\n\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.event.TableModelEvent;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * shows a list of projects in a table\n */\npublic class ProjectPanel extends JPanel implements Model.Listener {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate static final int MAGIC_NO_DUE_DATE = 9999; // magic days left value if no due date\n\n\n\n\n\n\t/** The button panel. */\n\tprivate JPanel buttonPanel = null;\n\n\t/** The project table. */\n\tprivate JTable projectTable;\n\n\t/** The project status combo box. */\n\tprivate final JComboBox<String> projectStatusComboBox = new JComboBox<String>();\n\n\t/**\n\t * constructor\n\t */\n\tpublic ProjectPanel() {\n\t\tsuper();\n\n\t\t// listen for task model changes\n\t\tTaskModel.getReference().addListener(this);\n\n\t\ttry {\n\t\t\tinitComponents();\n\t\t\trefresh();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t}\n\t\n\tpublic void cleanup(){\n\t\tTaskModel.getReference().removeListener(this);\n\t}\n\n\t/**\n\t * project change requested- bring up the project editor\n\t * \n\t */\n\tprivate void projectChangeRequested() {\n\n\t\tint row = projectTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn;\n\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\tInteger projectId = (Integer) tm.getValueAt(row, 0);\n\n\t\ttry {\n\t\t\tProject p = TaskModel.getReference().getProject(projectId.intValue());\n\t\t\tif (p == null)\n\t\t\t\treturn;\n\n\t\t\tnew ProjectView(p, ProjectView.Action.CHANGE, null).showView();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Clone seleced project\n\t * \n\t */\n\tprivate void cloneSelectedProject() {\n\n\t\t// get prject id from column 0 of selected row\n\t\tint row = projectTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn;\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\tInteger projectId = (Integer) tm.getValueAt(row, 0);\n\n\t\ttry {\n\t\t\tProject p = TaskModel.getReference().getProject(projectId.intValue());\n\t\t\tif (p == null)\n\t\t\t\treturn;\n\n\t\t\tnew ProjectView(p, ProjectView.Action.CLONE, null).showView();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Close selected project\n\t * \n\t * */\n\tprivate void closeSelectedProject() {\n\n\t\tint row = projectTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn;\n\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\tInteger projectId = (Integer) tm.getValueAt(row, 0);\n\t\ttry {\n\t\t\tTaskModel.getReference().closeProject(projectId.intValue());\n\t\t} catch (Warning w) {\n\t\t\tErrmsg.getErrorHandler().notice(w.getMessage());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * delete selected task\n\t * */\n\tprivate void deleteActionPerformed() {\n\n\t\tint row = projectTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn;\n\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\tInteger projectId = (Integer) tm.getValueAt(row, 0);\n\n\t\t// confirm delete\n\t\tint ret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t.getResourceString(\"Really_delete_number_\")\n\t\t\t\t+ \" \" + projectId, Resource.getResourceString(\"Confirm_Delete\"), JOptionPane.YES_NO_OPTION);\n\t\tif (ret == JOptionPane.YES_OPTION) {\n\t\t\t// delete the task\n\t\t\ttry {\n\t\t\t\tTaskModel.getReference().deleteProject(projectId.intValue());\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * get the button panel\n\t * \n\t * @return the button panel\n\t */\n\tprivate JPanel getButtonPanel() {\n\t\tif (buttonPanel == null) {\n\t\t\tbuttonPanel = new JPanel();\n\t\t\tbuttonPanel.setLayout(new FlowLayout());\n\n\t\t\tJButton addbutton = new JButton();\n\t\t\taddbutton.setText(Resource.getResourceString(\"Add\"));\n\t\t\taddbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Add16.gif\"));\n\t\t\taddbutton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnew ProjectView(null, ProjectView.Action.ADD, null)\n\t\t\t\t\t\t\t\t.showView();\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(addbutton, null);\n\n\t\t\tJButton changebutton1 = new JButton();\n\t\t\tchangebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Edit16.gif\"));\n\t\t\tchangebutton1.setText(Resource.getResourceString(\"Change\"));\n\t\t\tchangebutton1\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tprojectChangeRequested();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\tbuttonPanel.add(changebutton1, null);\n\n\t\t\tJButton deletebutton1 = new JButton();\n\t\t\tdeletebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Delete16.gif\"));\n\t\t\tdeletebutton1.setText(Resource.getResourceString(\"Delete\"));\n\t\t\tdeletebutton1\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tdeleteActionPerformed();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\tbuttonPanel.add(deletebutton1, null);\n\n\t\t\tJButton closebutton1 = new JButton();\n\t\t\tclosebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/greenlight.gif\"));\n\t\t\tclosebutton1.setText(Resource.getResourceString(\"Close\"));\n\t\t\tclosebutton1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tcloseSelectedProject();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(closebutton1, null);\n\n\t\t\tJButton clonebutton1 = new JButton();\n\t\t\tclonebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Copy16.gif\"));\n\t\t\tclonebutton1.setText(Resource.getResourceString(\"Clone\"));\n\t\t\tclonebutton1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tcloneSelectedProject();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(clonebutton1, null);\n\n\t\t}\n\t\treturn buttonPanel;\n\t}\n\n\t/**\n\t * initialize the UI\n\t * \n\t * @throws Exception\n\t */\n\tprivate void initComponents() throws Exception {\n\n\t\tthis.setLayout(new java.awt.GridBagLayout());\n\n\t\t/*\n\t\t * Filter Panel\n\t\t */\n\t\tFlowLayout flowLayout = new FlowLayout();\n\t\tflowLayout.setAlignment(java.awt.FlowLayout.LEFT);\n\t\tJPanel filterPanel = new JPanel();\n\t\tfilterPanel.setLayout(flowLayout);\n\n\t\tJLabel statusLabel = new JLabel();\n\t\tstatusLabel.setText(Resource.getResourceString(\"Status\") + \":\");\n\t\tfilterPanel.add(statusLabel, null);\n\n\t\tprojectStatusComboBox.removeAllItems();\n\t\tprojectStatusComboBox.addItem(Resource.getResourceString(\"All\"));\n\t\tprojectStatusComboBox.addItem(Resource.getResourceString(\"OPEN\"));\n\t\tprojectStatusComboBox.addItem(Resource.getResourceString(\"CLOSED\"));\n\t\tprojectStatusComboBox.setSelectedItem(Resource\n\t\t\t\t.getResourceString(\"OPEN\"));\n\t\tprojectStatusComboBox\n\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tfilterPanel.add(projectStatusComboBox, null);\n\n\t\tthis.add(filterPanel, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.HORIZONTAL));\n\n\t\t/*\n\t\t * project table\n\t\t */\n\t\tJScrollPane tableScroll = new JScrollPane();\n\t\tprojectTable = new JTable();\n\n\n\n\t\t// use a sorted table model\n\t\tprojectTable.setModel(new TableSorter(new String[] {\n\t\t\t\tResource.getResourceString(\"Item_#\"),\n\t\t\t\tResource.getResourceString(\"Category\"),\n\t\t\t\tResource.getResourceString(\"Status\"),\n\t\t\t\tResource.getResourceString(\"Start_Date\"),\n\t\t\t\tResource.getResourceString(\"Due_Date\"),\n\t\t\t\tResource.getResourceString(\"total_tasks\"),\n\t\t\t\tResource.getResourceString(\"open_tasks\"),\n\t\t\t\tResource.getResourceString(\"Days_Left\"),\n\t\t\t\tResource.getResourceString(\"Description\") }, new Class[] {\n\t\t\t\tjava.lang.Integer.class, java.lang.String.class, String.class,\n\t\t\t\tDate.class, Date.class, java.lang.Integer.class, Integer.class,\n\t\t\t\tInteger.class, java.lang.String.class }));\n\n\t\t// popup menu\n\t\tnew PopupMenuHelper(projectTable, new PopupMenuHelper.Entry[] {\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tnew ProjectView(null, ProjectView.Action.ADD, null)\n\t\t\t\t\t\t\t\t\t.showView();\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, \"Add\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tprojectChangeRequested();\n\t\t\t\t\t}\n\t\t\t\t}, \"Change\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tcloneSelectedProject();\n\t\t\t\t\t}\n\t\t\t\t}, \"Clone\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tdeleteActionPerformed();\n\t\t\t\t\t}\n\t\t\t\t}, \"Delete\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tcloseSelectedProject();\n\t\t\t\t\t}\n\t\t\t\t}, \"Close\") });\n\n\t\tprojectTable.getColumnModel().getColumn(0).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(1).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(2).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(3).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(5).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(6).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(7).setPreferredWidth(80);\n\t\tprojectTable.getColumnModel().getColumn(8).setPreferredWidth(400);\n\n\t\tprojectTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\n\t\t// set up for sorting when a column header is clicked\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(projectTable);\n\n\t\t// clear all rows\n\t\ttm.setRowCount(0);\n\t\ttm.tableChanged(new TableModelEvent(tm));\n\n\t\ttableScroll.setViewportView(projectTable);\n\n\t\tprojectTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);\n\n\t\tprojectTable.setShowGrid(true);\n\t\tprojectTable.setIntercellSpacing(new Dimension(1, 1));\n\n\n\t\tthis.add(tableScroll, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * button panel\n\t\t */\n\t\tthis.add(getButtonPanel(), GridBagConstraintsFactory.create(0, 2));\n\n\t}\n\n\t/**\n\t * Prints the project list\n\t */\n\tpublic void print() {\n\n\t\ttry {\n\t\t\tTablePrinter.printTable(projectTable);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * refresh when the task model changes\n\t */\n\tpublic void refresh() {\n\n\t\t// clear all table rows\n\t\tTableSorter tm = (TableSorter) projectTable.getModel();\n\t\ttm.setRowCount(0);\n\t\ttm.tableChanged(new TableModelEvent(tm));\n\n\t\tString pstatfilt = (String) projectStatusComboBox.getSelectedItem();\n\n\t\ttry {\n\n\t\t\t// add projects to project table\n\t\t\tCollection<Project> projects = TaskModel.getReference()\n\t\t\t\t\t.getProjects();\n\t\t\tfor (Project project : projects) {\n\n\t\t\t\t// filter by status\n\t\t\t\tif (!pstatfilt.equals(Resource.getResourceString(\"All\"))\n\t\t\t\t\t\t&& !pstatfilt.equals(project.getStatus()))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// filter by category\n\t\t\t\tif (!CategoryModel.getReference()\n\t\t\t\t\t\t.isShown(project.getCategory()))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// if we get here - we are displaying this task as a row\n\t\t\t\t// so fill in an array of objects for the row\n\t\t\t\tObject[] ro = new Object[10];\n\t\t\t\tro[0] = Integer.valueOf(project.getKey());\n\t\t\t\tro[1] = project.getCategory();\n\t\t\t\tro[2] = project.getStatus();\n\t\t\t\tro[3] = project.getStartDate();\n\t\t\t\tro[4] = project.getDueDate();\n\n\t\t\t\t// number of tasks\n\t\t\t\tCollection<Task> ptasks = TaskModel.getReference().getTasks(\n\t\t\t\t\t\tproject.getKey());\n\t\t\t\tro[5] = Integer.valueOf(ptasks.size());\n\n\t\t\t\t// open tasks\n\t\t\t\tint open = 0;\n\t\t\t\tfor (Task pt : ptasks) {\n\t\t\t\t\tif (!TaskModel.isClosed(pt)) {\n\t\t\t\t\t\topen++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tro[6] = Integer.valueOf(open);\n\n\t\t\t\t// days left\n\t\t\t\tro[7] = Integer.valueOf(0);\n\t\t\t\tif (ro[4] == null)\n\t\t\t\t\t// if no due date \n\t\t\t\t\tro[7] = Integer.valueOf(MAGIC_NO_DUE_DATE);\n\t\t\t\telse {\n\t\t\t\t\tDate dd = (Date) ro[4];\n\t\t\t\t\tro[7] = Integer.valueOf(DateUtil.daysLeft(dd));\n\t\t\t\t}\n\n\t\t\t\t// strip newlines from the description\n\t\t\t\tString de = project.getDescription();\n\t\t\t\tStringBuffer tmp = new StringBuffer();\n\t\t\t\tfor (int i = 0; de != null && i < de.length(); i++) {\n\t\t\t\t\tchar c = de.charAt(i);\n\t\t\t\t\tif (c == '\\n' || c == '\\r') {\n\t\t\t\t\t\ttmp.append(' ');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\ttmp.append(c);\n\t\t\t\t}\n\t\t\t\tro[8] = tmp.toString();\n\n\t\t\t\t// add the task row to table\n\t\t\t\ttm.addRow(ro);\n\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/ProjectTreePanel.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.task;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.GridLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashMap;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JCheckBox;\nimport javax.swing.JMenuItem;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTree;\nimport javax.swing.event.TreeSelectionEvent;\nimport javax.swing.event.TreeSelectionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeCellRenderer;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\nimport javax.swing.tree.TreeSelectionModel;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.KeyedEntity;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * Presents a split pane showing a tree of projects, subprojects, and tasks on\n * the left and the select item's details on the right.\n */\npublic class ProjectTreePanel extends JPanel implements TreeSelectionListener,\n\t\tMouseListener, Model.Listener, Prefs.Listener {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t// cache to prevent painting of project tree from accessing the db\n\tprivate final HashMap<Integer, Integer> daysLeftCache = new HashMap<Integer, Integer>();\n\n\t/**\n\t * Custom Tree Cell Renderer that shows empty projects as closed folders\n\t * instead of leaf icons.\n\t */\n\tprivate class ProjectTreeCellRenderer extends DefaultTreeCellRenderer {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\t\tprivate final ImageIcon redIcon = IconHelper.getIcon(\n\t\t\t\t\"/resource/red.png\");\n\t\tprivate final ImageIcon orangeIcon = IconHelper.getIcon(\"/resource/orange.png\");\n\t\tprivate final ImageIcon yellowIcon = IconHelper.getIcon(\"/resource/yellow.png\");\n\t\tprivate final ImageIcon greenIcon = IconHelper.getIcon(\"/resource/green.png\");\n\t\tprivate final ImageIcon emptyIcon = IconHelper.getIcon(\"/resource/empty.png\");\n\t\tprivate final ImageIcon doneIcon = IconHelper.getIcon(\"/resource/done.png\");\n\n\t\t@Override\n\t\tpublic Component getTreeCellRendererComponent(JTree tree, Object value,\n\t\t\t\tboolean sel, boolean expanded, boolean leaf, int row,\n\t\t\t\tboolean has_focus) {\n\n\t\t\tsuper.getTreeCellRendererComponent(tree, value, sel, expanded,\n\t\t\t\t\tleaf, row, has_focus);\n\n\t\t\t// get the tree model node\n\t\t\tif (value instanceof DefaultMutableTreeNode) {\n\t\t\t\tDefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;\n\n\t\t\t\t// get the Borg Node object, if any\n\t\t\t\tif (treeNode.getUserObject() != null\n\t\t\t\t\t\t&& treeNode.getUserObject() instanceof Node) {\n\t\t\t\t\tNode node = (Node) treeNode.getUserObject();\n\n\t\t\t\t\t// determine the icon for a task based on days left until\n\t\t\t\t\t// next due item\n\t\t\t\t\tif (node.getEntity() instanceof Task) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tTask task = (Task) node.getEntity();\n\n\t\t\t\t\t\tif (TaskModel.isClosed(((Task) node.getEntity()))) {\n\t\t\t\t\t\t\tthis.setIcon(doneIcon);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tint daysLeft;\n\t\t\t\t\t\t\t\tInteger dl = daysLeftCache.get(Integer.valueOf(task.getKey()));\n\t\t\t\t\t\t\t\tif( dl == null )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdaysLeft = TaskModel.getReference()\n\t\t\t\t\t\t\t\t\t\t\t.daysLeft(task);\n\t\t\t\t\t\t\t\t\tdaysLeftCache.put(Integer.valueOf(task.getKey()), Integer.valueOf(daysLeft));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdaysLeft = dl.intValue();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (daysLeft < Prefs\n\t\t\t\t\t\t\t\t\t\t.getIntPref(PrefName.RED_DAYS))\n\t\t\t\t\t\t\t\t\tthis.setIcon(redIcon);\n\t\t\t\t\t\t\t\telse if (daysLeft < Prefs\n\t\t\t\t\t\t\t\t\t\t.getIntPref(PrefName.ORANGE_DAYS))\n\t\t\t\t\t\t\t\t\tthis.setIcon(orangeIcon);\n\t\t\t\t\t\t\t\telse if (daysLeft < Prefs\n\t\t\t\t\t\t\t\t\t\t.getIntPref(PrefName.YELLOW_DAYS))\n\t\t\t\t\t\t\t\t\tthis.setIcon(yellowIcon);\n\t\t\t\t\t\t\t\telse if (daysLeft == TaskModel.NO_DAYS_VALUE)\n\t\t\t\t\t\t\t\t\tthis.setIcon(emptyIcon);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tthis.setIcon(greenIcon);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\t\t\t\t// don't stop processing\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// if the Borg Entity is a Project and the node is a leaf in\n\t\t\t\t\t// the tree, then show\n\t\t\t\t\t// the closed folder icon\n\t\t\t\t\tif (leaf && node.getEntity() instanceof Project) {\n\t\t\t\t\t\tthis.setIcon(closedIcon);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t/**\n\t * A Node in the tree that contains the visible node name and the related\n\t * object\n\t */\n\tstatic private class Node {\n\n\t\t/** The entity name. */\n\t\tprivate String name;\n\n\t\t/** The entity */\n\t\tprivate KeyedEntity<?> entity;\n\n\t\t/**\n\t\t * Instantiates a new node.\n\t\t * \n\t\t * @param name\n\t\t *            the name\n\t\t * @param o\n\t\t *            the entity\n\t\t */\n\t\tpublic Node(String name, KeyedEntity<?> o) {\n\t\t\tsuper();\n\t\t\tthis.name = name;\n\t\t\tentity = o;\n\t\t}\n\n\t\t/**\n\t\t * Gets the name.\n\t\t * \n\t\t * @return the name\n\t\t */\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\t/**\n\t\t * Gets the entity.\n\t\t * \n\t\t * @return the entity\n\t\t */\n\t\tpublic KeyedEntity<?> getEntity() {\n\t\t\treturn entity;\n\t\t}\n\n\t\t/**\n\t\t * Sets the name.\n\t\t * \n\t\t * @param name\n\t\t *            the new name\n\t\t */\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t/**\n\t\t * Sets the entity.\n\t\t * \n\t\t * @param obj\n\t\t *            the new entity\n\t\t */\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic void setEntity(KeyedEntity<?> obj) {\n\t\t\tthis.entity = obj;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see java.lang.Object#toString()\n\t\t */\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn name;\n\t\t}\n\n\t}\n\n\t/**\n\t * Expand or Collapse all nodes in the tree under a given node\n\t * \n\t * @param tree\n\t *            the tree\n\t * @param parent\n\t *            the start node\n\t * @param expand\n\t *            the expand\n\t */\n\tstatic private void expandOrCollapseSubTree(JTree tree, TreePath parent,\n\t\t\tboolean expand) {\n\n\t\t// recurse to call this method for every child\n\t\tTreeNode node = (TreeNode) parent.getLastPathComponent();\n\t\tif (node.getChildCount() >= 0) {\n\t\t\tfor (Enumeration<?> e = node.children(); e.hasMoreElements();) {\n\t\t\t\tTreeNode n = (TreeNode) e.nextElement();\n\t\t\t\tTreePath path = parent.pathByAddingChild(n);\n\t\t\t\texpandOrCollapseSubTree(tree, path, expand);\n\t\t\t}\n\t\t}\n\n\t\t// Expansion or collapse must be done bottom-up, so we do the actual\n\t\t// work\n\t\t// after the recursive call\n\t\tif (expand) {\n\t\t\ttree.expandPath(parent);\n\t\t} else {\n\t\t\ttree.collapsePath(parent);\n\t\t}\n\t}\n\n\t/**\n\t * true if the state of the tree is expanded - used to refresh in the same\n\t * state\n\t */\n\tprivate boolean isExpanded = true;\n\n\t/** The project popup menu. */\n\tprivate final JPopupMenu projmenu = new JPopupMenu();\n\n\t/** The root node popup menu */\n\tprivate final JPopupMenu rootmenu = new JPopupMenu();\n\n\t/** The show closed. */\n\tprivate final JCheckBox showClosedCheckBox = new JCheckBox(\n\t\t\tResource.getResourceString(\"show_closed\"));\n\n\tprivate final JCheckBox showClosedTasksCheckBox = new JCheckBox(\n\t\t\tResource.getResourceString(\"show_closed_tasks\"));\n\n\t/** The tree. */\n\tprivate JTree projectTree = null;\n\n\t/** The tree scroll pane. */\n\tprivate JScrollPane treeScrollPane = null;\n\n\t/** The entity Scroll Pane. */\n\tprivate final JScrollPane entityScrollPane = new JScrollPane(new JPanel());\n\n\t/**\n\t * constructor\n\t */\n\tpublic ProjectTreePanel() {\n\n\t\tsuper(new GridLayout(0, 1));\n\n\t\t// listen for task model changes\n\t\tTaskModel.getReference().addListener(this);\n\n\t\t// Create the nodes.\n\t\tDefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(\n\t\t\t\tResource.getResourceString(\"projects\"));\n\t\tcreateNodes(rootNode);\n\n\t\t// Create a tree that allows one selection at a time.\n\t\tprojectTree = new JTree(rootNode);\n\t\tprojectTree.getSelectionModel().setSelectionMode(\n\t\t\t\tTreeSelectionModel.SINGLE_TREE_SELECTION);\n\n\t\tprojectTree.setCellRenderer(new ProjectTreeCellRenderer());\n\n\t\t// Listen for when the selection changes.\n\t\tprojectTree.addTreeSelectionListener(this);\n\n\t\t// Create the scroll pane and add the tree to it.\n\t\ttreeScrollPane = new JScrollPane(projectTree);\n\n\t\t// pane containing tree + show closed check box\n\t\tJPanel treePane = new JPanel();\n\t\ttreePane.setLayout(new GridBagLayout());\n\t\ttreePane.add(treeScrollPane, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\t\ttreePane.add(showClosedCheckBox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH));\n\t\ttreePane.add(showClosedTasksCheckBox,\n\t\t\t\tGridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\n\t\t// Add the scroll panes to a split pane.\n\t\tJSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);\n\t\tsplitPane.setTopComponent(treePane);\n\t\tsplitPane.setBottomComponent(entityScrollPane);\n\n\t\tDimension minimumSize = new Dimension(200, 50);\n\t\tentityScrollPane.setMinimumSize(minimumSize);\n\t\ttreeScrollPane.setMinimumSize(minimumSize);\n\t\tsplitPane.setDividerLocation(250);\n\n\t\tadd(splitPane);\n\n\t\tprojectTree.addMouseListener(this);\n\n\t\t/*\n\t\t * root node popup menu\n\t\t */\n\t\tJMenuItem jm = rootmenu.add(Resource.getResourceString(\"Add\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"project\"));\n\t\tjm.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\t// show a new project editor\n\t\t\t\t\tProjectView pv = new ProjectView(null,\n\t\t\t\t\t\t\tProjectView.Action.ADD, null);\n\t\t\t\t\tentityScrollPane.setViewportView(pv);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t\tJMenuItem jrt = rootmenu.add(Resource.getResourceString(\"Add\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"task\"));\n\t\tjrt.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\taddTask();\n\t\t\t}\n\n\t\t});\n\n\t\tJMenuItem jmex = rootmenu.add(Resource.getResourceString(\"expand\"));\n\t\tjmex.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tisExpanded = true;\n\t\t\t\texpandOrCollapseAll(isExpanded);\n\t\t\t}\n\n\t\t});\n\n\t\tJMenuItem jmcol = rootmenu.add(Resource.getResourceString(\"collapse\"));\n\t\tjmcol.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tisExpanded = false;\n\t\t\t\texpandOrCollapseAll(isExpanded);\n\t\t\t}\n\n\t\t});\n\n\t\t/*\n\t\t * project node popup menu\n\t\t */\n\t\tJMenuItem jm2 = projmenu.add(Resource.getResourceString(\"Add\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"task\"));\n\t\tjm2.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\taddTask();\n\t\t\t}\n\n\t\t});\n\t\tJMenuItem jm3 = projmenu.add(Resource.getResourceString(\"Add\") + \" \"\n\t\t\t\t+ Resource.getResourceString(\"project\"));\n\t\tjm3.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\taddSubProject();\n\t\t\t}\n\n\t\t});\n\n\t\tshowClosedCheckBox.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\trefresh();\n\t\t\t}\n\t\t});\n\n\t\tshowClosedTasksCheckBox.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\trefresh();\n\t\t\t}\n\t\t});\n\n\t\texpandOrCollapseAll(isExpanded);\n\n\t\tPrefs.addListener(this);\n\t}\n\n\t/**\n\t * Add a new sub project as a child of the current project and show an\n\t * editor for it\n\t */\n\tprivate void addSubProject() {\n\n\t\tObject o = getSelectedEntity();\n\t\tif (o == null)\n\t\t\treturn;\n\t\tif (o instanceof Project) {\n\t\t\tProject p = (Project) o;\n\t\t\ttry {\n\t\t\t\tProjectView pv;\n\t\t\t\ttry {\n\t\t\t\t\tpv = new ProjectView(null, ProjectView.Action.ADD,\n\t\t\t\t\t\t\tInteger.valueOf(p.getKey()));\n\t\t\t\t\tentityScrollPane.setViewportView(pv);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Add a new sub task as a child of the current project and show an editor\n\t * for it\n\t */\n\tprivate void addTask() {\n\t\tInteger pid = null;\n\t\tObject o = getSelectedEntity();\n\t\tif (o instanceof Project) {\n\t\t\tProject p = (Project) o;\n\t\t\tpid = p.getKey();\n\t\t}\n\t\ttry {\n\t\t\tTaskView pv = new TaskView(null, TaskView.Action.ADD,\n\t\t\t\t\tpid);\n\t\t\tentityScrollPane.setViewportView(pv);\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t}\n\n\t}\n\n\t/**\n\t * Adds the project children to the tree\n\t * \n\t * @param p\n\t *            the project\n\t * @param node\n\t *            the project node\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tprivate void addProjectChildren(Project p, DefaultMutableTreeNode node)\n\t\t\tthrows Exception {\n\n\t\tCollection<Task> tasks = null;\n\t\tif( p != null )\n\t\t\ttasks = TaskModel.getReference().getTasks(p.getKey());\n\t\telse {\n\t\t\ttasks = TaskModel.getReference().getTasks();\n\t\t}\n\n\t\tfor (Task task : tasks) {\n\n\t\t\t// don't add tasks that have a project to the top node\n\t\t\tif( p == null && task.getProject() != null) continue;\n\n\t\t\tif (!CategoryModel.getReference().isShown(task.getCategory()))\n\t\t\t\tcontinue;\n\n\t\t\t// filter out closed projects if needed\n\t\t\tif (!showClosedTasksCheckBox.isSelected()\n\t\t\t\t\t&& TaskModel.isClosed(task))\n\t\t\t\tcontinue;\n\n\t\t\tString taskdesc = task.getSummary();\n\n\t\t\tif (Prefs.getBoolPref(PrefName.TASK_TREE_SHOW_STATUS)) {\n\t\t\t\tnode.add(new DefaultMutableTreeNode(new Node(\"[\"\n\t\t\t\t\t\t+ task.getKey() + \"-\" + task.getState() + \"] \"\n\t\t\t\t\t\t+ taskdesc, task)));\n\t\t\t} else {\n\t\t\t\tnode.add(new DefaultMutableTreeNode(new Node(taskdesc, task)));\n\t\t\t}\n\t\t}\n\n\t\tif( p == null ) return;\n\n\t\tCollection<Project> subpcoll = TaskModel.getReference().getSubProjects(\n\t\t\t\tp.getKey());\n\t\tfor (Project project : subpcoll) {\n\t\t\tif (!CategoryModel.getReference().isShown(project.getCategory()))\n\t\t\t\tcontinue;\n\t\t\tif (!showClosedCheckBox.isSelected() && TaskModel.isClosed(project))\n\t\t\t\tcontinue;\n\t\t\tDefaultMutableTreeNode subnode = new DefaultMutableTreeNode(\n\t\t\t\t\tnew Node(project.getDescription(), project));\n\t\t\tnode.add(subnode);\n\t\t\taddProjectChildren(project, subnode);\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates the entire tree by adding all items in the task model\n\t * \n\t * @param top\n\t *            the root node\n\t */\n\tprivate void createNodes(DefaultMutableTreeNode top) {\n\n\t\tCollection<Project> projects;\n\t\ttry {\n\t\t\tprojects = TaskModel.getReference().getProjects();\n\t\t\tfor (Project p : projects) {\n\n\t\t\t\t// filter out closed projects if needed\n\t\t\t\tif (!showClosedCheckBox.isSelected() && TaskModel.isClosed(p))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// filter by caegory\n\t\t\t\tif (!CategoryModel.getReference().isShown(p.getCategory()))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// don't add sub-projects - they are added later\n\t\t\t\tif (p.getParent() != null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tDefaultMutableTreeNode pnode = new DefaultMutableTreeNode(\n\t\t\t\t\t\tnew Node(p.getDescription(), p));\n\n\t\t\t\t// add the top level project node\n\t\t\t\ttop.add(pnode);\n\n\t\t\t\t// add the project's children\n\t\t\t\taddProjectChildren(p, pnode);\n\t\t\t}\n\n\t\t\t// add project-less nodes\n\t\t\taddProjectChildren(null, top);\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Expand or collapse all nodes\n\t * \n\t * @param expand\n\t *            if true - expand, else collapse\n\t */\n\tpublic void expandOrCollapseAll(boolean expand) {\n\t\tTreeNode root = (TreeNode) projectTree.getModel().getRoot();\n\n\t\t// Traverse tree from root\n\t\texpandOrCollapseSubTree(projectTree, new TreePath(root), expand);\n\t}\n\n\t/**\n\t * Gets the selected eneity.\n\t * \n\t * @return the selected entity\n\t */\n\tprivate KeyedEntity<?> getSelectedEntity() {\n\t\tDefaultMutableTreeNode node = (DefaultMutableTreeNode) projectTree\n\t\t\t\t.getLastSelectedPathComponent();\n\t\tif (node == null)\n\t\t\treturn null;\n\n\t\tObject nodeobj = node.getUserObject();\n\t\tif (!(nodeobj instanceof Node))\n\t\t\treturn null;\n\n\t\tNode mynode = (Node) node.getUserObject();\n\n\t\treturn mynode.getEntity();\n\n\t}\n\n\t/**\n\t * handle right mouse click events and popup the appropriate menu\n\t */\n\t@Override\n\tpublic void mouseClicked(MouseEvent e) {\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseEntered(MouseEvent arg0) {\n\t\t// empty\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseExited(MouseEvent arg0) {\n\t\t// empty\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mousePressed(MouseEvent e) {\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseReleased(MouseEvent e) {\n\t\tif (e.getButton() == MouseEvent.BUTTON1)\n\t\t\treturn;\n\n\t\tTreePath selPath = projectTree.getPathForLocation(e.getX(), e.getY());\n\t\tif (selPath == null)\n\t\t\treturn;\n\t\tDefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath\n\t\t\t\t.getLastPathComponent();\n\t\tif (node == null)\n\t\t\treturn;\n\n\t\tprojectTree.setSelectionPath(selPath);\n\t\tObject nodeobj = node.getUserObject();\n\t\tif (!(nodeobj instanceof Node)) {\n\t\t\trootmenu.show(this, e.getX(), e.getY());\n\t\t\treturn;\n\t\t}\n\n\t\tNode mynode = (Node) node.getUserObject();\n\n\t\tObject o = mynode.getEntity();\n\t\tif (o == null)\n\t\t\treturn;\n\t\tif (o instanceof Project)\n\t\t\tprojmenu.show(this, e.getX(), e.getY());\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t\n\t\tdaysLeftCache.clear();\n\t\trefresh();\n\n\t\t// check if the object being edited has been deleted\n\t\tObject o = entityScrollPane.getViewport().getView();\n\t\tif (o instanceof ProjectView && event.getObject() instanceof Project) {\n\t\t\tProjectView pv = (ProjectView) o;\n\t\t\tProject p = (Project) event.getObject();\n\t\t\tif (event.getAction() == ChangeAction.DELETE\n\t\t\t\t\t&& p.getKey() == pv.getShownId()) {\n\t\t\t\tentityScrollPane.setViewportView(null);\n\t\t\t}\n\t\t} else if (o instanceof TaskView && event.getObject() instanceof Task) {\n\t\t\tTaskView pv = (TaskView) o;\n\t\t\tTask p = (Task) event.getObject();\n\t\t\tif (event.getAction() == ChangeAction.DELETE\n\t\t\t\t\t&& p.getKey() == pv.getShownId()) {\n\t\t\t\tentityScrollPane.setViewportView(null);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * refresh the entire tree from the task model\n\t */\n\tpublic void refresh() {\n\t\tDefaultMutableTreeNode top = new DefaultMutableTreeNode(\n\t\t\t\tResource.getResourceString(\"projects\"));\n\t\tcreateNodes(top);\n\t\t// Create a tree that allows one selection at a time.\n\t\tprojectTree = new JTree(top);\n\t\tprojectTree.setCellRenderer(new ProjectTreeCellRenderer());\n\n\t\tprojectTree.getSelectionModel().setSelectionMode(\n\t\t\t\tTreeSelectionModel.SINGLE_TREE_SELECTION);\n\n\t\t// Listen for when the selection changes.\n\t\tprojectTree.addTreeSelectionListener(this);\n\t\ttreeScrollPane.setViewportView(projectTree);\n\t\tprojectTree.addMouseListener(this);\n\t\texpandOrCollapseAll(isExpanded);\n\n\t}\n\n\t/**\n\t * handle node selection in the tree. show the appropriate editor in the\n\t * right pane\n\t * \n\t * @param e\n\t *            the selection event\n\t */\n\t@Override\n\tpublic void valueChanged(TreeSelectionEvent e) {\n\t\tObject o = getSelectedEntity();\n\t\tif (o == null) {\n\t\t\tentityScrollPane.setViewportView(null);\n\t\t} else if (o instanceof Task) {\n\t\t\tTask t = (Task) o;\n\t\t\ttry {\n\t\t\t\tTaskView tv = new TaskView(t, TaskView.Action.CHANGE,\n\t\t\t\t\t\tt.getProject());\n\t\t\t\tentityScrollPane.setViewportView(tv);\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (o instanceof Project) {\n\t\t\tProject p = (Project) o;\n\t\t\ttry {\n\t\t\t\tProjectView pv = new ProjectView(p, ProjectView.Action.CHANGE,\n\t\t\t\t\t\tnull);\n\t\t\t\tentityScrollPane.setViewportView(pv);\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\t/**\n\t * update when prefs change\n\t */\n\tpublic void prefsChanged() {\n\t\trefresh();\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/ProjectView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.task;\n\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\n\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\nimport javax.swing.SwingConstants;\nimport javax.swing.border.TitledBorder;\n\nimport com.toedter.calendar.JDateChooser;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.link.LinkPanel;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PlainDateEditor;\n\n/**\n * UI for viewing and editing a single project\n */\npublic class ProjectView extends DockableView {\n\n\t@Override\n\tprotected void cleanUp() {\n\t\tsuper.cleanUp();\n\t\tif( linkPanel != null)\n\t\t\tlinkPanel.cleanup();\n\t\tif( taskPanel != null )\n\t\t\ttaskPanel.cleanUp();\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Action that we are invoking this editor for\n\t */\n\tpublic enum Action {\n\n\t\t/** add new project */\n\t\tADD,\n\t\t/** edit existing project */\n\t\tCHANGE,\n\t\t/** close existing project and edit as new */\n\t\tCLONE\n\t}\n\n\t/**\n\t * Gets the project id from the text in the parent project combo box\n\t * \n\t * @param s\n\t *            the string from the parent combo box\n\t * \n\t * @return the project id\n\t * \n\t * @throws Exception\n\t * \n\t */\n\tstatic private Integer getProjectId(String s) throws Exception {\n\t\t// project id is the number before the colon\n\t\tint i = s.indexOf(\":\");\n\t\tif (i == -1)\n\t\t\tthrow new Exception(\"Cannot parse project label\");\n\t\tString ss = s.substring(0, i);\n\n\t\tint pid = Integer.parseInt(ss);\n\t\treturn Integer.valueOf(pid);\n\n\t}\n\n\t/**\n\t * creates a string to show in the parent project combo box that contains\n\t * project id and name\n\t * \n\t * @param p\n\t *            the project\n\t * \n\t * @return the project string\n\t */\n\tstatic private String getProjectString(Project p) {\n\t\tString desc = p.getDescription();\n\t\tif( desc != null && desc.length() > 20)\n\t\t{\n\t\t\tdesc = desc.substring(0, 20) + \"...\";\n\t\t}\n\t\treturn p.getKey() + \":\" + desc;\n\t}\n\n\t/** The category box. */\n\tprivate JComboBox<String> categoryBox = null;\n\n\t/** The days left text. */\n\tprivate JTextField daysLeftText = null;\n\n\t/** The description. */\n\tprivate JTextField description;\n\n\t/** The due date chooser. */\n\tprivate JDateChooser dueDateChooser;\n\n\t/** The project id text. */\n\tprivate JTextField projectIdText;\n\n\t/** The link panel. */\n\tprivate final LinkPanel linkPanel = new LinkPanel();\n\n\t/** The open task count. */\n\tprivate JTextField openTaskCount = null;\n\n\t/** The parent project combo box. */\n\tprivate final JComboBox<String> parentProjectComboBox = new JComboBox<String>();\n\n\t/** The start date chooser. */\n\tprivate JDateChooser startDateChooser;\n\n\t/** The status combo box. */\n\tprivate JComboBox<String> statusComboBox;\n\n\t/** The task border. */\n\tprivate JPanel taskBorder = null;\n\n\t/** The task panel. */\n\tprivate TaskListPanel taskPanel = null;\n\n\t/** The window title. */\n\tprivate String windowTitle = \"\";\n\n\t/** The total task count. */\n\tprivate JTextField totalTaskCount = null;\n\n\t/**\n\t * constructor\n\t * \n\t * @param p\n\t *            the project\n\t * @param function\n\t *            the action being taken\n\t * @param parentId\n\t *            the parent project id, if any\n\t * \n\t * @throws Exception\n\t *             the exception\n\t */\n\tpublic ProjectView(Project p, Action function, Integer parentId) {\n\t\tsuper();\n\n\t\t// listen for task model changes\n\t\taddModel(TaskModel.getReference());\n\n\t\tinitComponents(); // init the GUI widgets\n\n\t\t// load the categories\n\t\ttry {\n\t\t\tCollection<String> cats = CategoryModel.getReference()\n\t\t\t\t\t.getCategories();\n\t\t\tIterator<String> it = cats.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tcategoryBox.addItem(it.next());\n\t\t\t}\n\t\t\tcategoryBox.setSelectedIndex(0);\n\n\t\t\t// show the project\n\t\t\tshowProject(function, p, parentId);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.DockableView#getFrameTitle()\n\t */\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn windowTitle;\n\t}\n\n\t/**\n\t * Initialize the UI.\n\t */\n\tprivate void initComponents()// GEN-BEGIN:initComponents\n\t{\n\n\t\tsetLayout(new GridBagLayout());\n\n\t\t/*\n\t\t * project info panel\n\t\t */\n\t\tJPanel projectInfoPanel = new JPanel();\n\t\tprojectInfoPanel.setLayout(new GridBagLayout());\n\n\t\tprojectInfoPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"ProjectInformation\")));\n\n\t\tprojectIdText = new JTextField();\n\t\tprojectIdText.setText(\"projectIdText\");\n\n\t\tdescription = new JTextField(10);\n\t\tJLabel lblItemNum = new JLabel();\n\t\tlblItemNum.setText(Resource.getResourceString(\"Item_#\"));\n\n\t\tdueDateChooser = new JDateChooser(new PlainDateEditor());\n\t\tJLabel lblStartDate = new JLabel();\n\t\tJLabel lblDueDate = new JLabel();\n\t\tstatusComboBox = new JComboBox<String>();\n\t\tJLabel catlabel = new JLabel();\n\n\t\tJLabel lblStatus = new JLabel();\n\t\tResourceHelper.setText(lblStatus, \"Status\");\n\t\tlblStatus.setLabelFor(statusComboBox);\n\n\t\tstartDateChooser = new JDateChooser(new PlainDateEditor());\n\t\tResourceHelper.setText(lblStartDate, \"Start_Date\");\n\t\tlblStartDate.setLabelFor(startDateChooser);\n\n\t\tResourceHelper.setText(lblDueDate, \"Due_Date\");\n\t\tlblDueDate.setLabelFor(dueDateChooser);\n\n\t\tResourceHelper.setText(catlabel, \"Category\");\n\t\tcategoryBox = new JComboBox<String>();\n\t\tcatlabel.setLabelFor(categoryBox);\n\n\t\tprojectInfoPanel\n\t\t\t\t.add(lblItemNum, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tprojectInfoPanel\n\t\t\t\t.add(lblStatus, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tprojectInfoPanel\n\t\t\t\t.add(catlabel, GridBagConstraintsFactory.create(0, 2,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tJLabel descLabel = new JLabel();\n\t\tdescLabel.setText(Resource.getResourceString(\"Description\"));\n\t\tprojectInfoPanel\n\t\t\t\t.add(descLabel, GridBagConstraintsFactory.create(0, 3,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tprojectInfoPanel\n\t\t\t\t.add(projectIdText, GridBagConstraintsFactory.create(1, 0,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tprojectInfoPanel.add(statusComboBox, GridBagConstraintsFactory.create(\n\t\t\t\t1, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tprojectInfoPanel.add(categoryBox, GridBagConstraintsFactory.create(1,\n\t\t\t\t2, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tGridBagConstraints dgbc = GridBagConstraintsFactory.create(1,\n\t\t\t\t3, GridBagConstraints.BOTH, 1.0, 0.0);\n\t\tdgbc.gridwidth = 5;\n\t\tdgbc.weightx = 0.0;\n\t\tprojectInfoPanel.add(description, dgbc);\n\n\t\tJLabel parentLabel = new JLabel(Resource.getResourceString(\"parent\"));\n\t\tprojectInfoPanel\n\t\t\t\t.add(parentLabel, GridBagConstraintsFactory.create(2, 0,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tprojectInfoPanel\n\t\t\t\t.add(lblStartDate, GridBagConstraintsFactory.create(2, 1,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\t\tprojectInfoPanel\n\t\t\t\t.add(lblDueDate, GridBagConstraintsFactory.create(2, 2,\n\t\t\t\t\t\tGridBagConstraints.BOTH));\n\n\t\tprojectInfoPanel.add(parentProjectComboBox, GridBagConstraintsFactory\n\t\t\t\t.create(3, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\tprojectInfoPanel.add(startDateChooser, GridBagConstraintsFactory\n\t\t\t\t.create(3, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\tprojectInfoPanel.add(dueDateChooser, GridBagConstraintsFactory.create(\n\t\t\t\t3, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJLabel daysLeftLabel = new JLabel();\n\t\tdaysLeftLabel.setText(Resource.getResourceString(\"Days_Left\"));\n\t\tdaysLeftLabel.setHorizontalTextPosition(SwingConstants.RIGHT);\n\t\tdaysLeftLabel.setHorizontalAlignment(SwingConstants.RIGHT);\n\t\tprojectInfoPanel.add(daysLeftLabel,\n\t\t\t\tGridBagConstraintsFactory.create(4, 0));\n\t\tJLabel totalLabel = new JLabel();\n\t\ttotalLabel.setText(Resource.getResourceString(\"total_tasks\"));\n\t\tprojectInfoPanel\n\t\t\t\t.add(totalLabel, GridBagConstraintsFactory.create(4, 1));\n\t\tJLabel openLabel = new JLabel();\n\t\topenLabel.setText(Resource.getResourceString(\"open_tasks\"));\n\t\tprojectInfoPanel.add(openLabel, GridBagConstraintsFactory.create(4, 2));\n\n\t\tdaysLeftText = new JTextField();\n\t\tdaysLeftText.setEditable(false);\n\t\tprojectInfoPanel.add(daysLeftText, GridBagConstraintsFactory.create(5,\n\t\t\t\t0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttotalTaskCount = new JTextField();\n\t\ttotalTaskCount.setEditable(false);\n\t\tprojectInfoPanel.add(totalTaskCount, GridBagConstraintsFactory.create(\n\t\t\t\t5, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\topenTaskCount = new JTextField();\n\t\topenTaskCount.setEditable(false);\n\t\tprojectInfoPanel.add(openTaskCount, GridBagConstraintsFactory.create(5,\n\t\t\t\t2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tadd(projectInfoPanel, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\t/*\n\t\t * button panel\n\t\t */\n\t\tJPanel buttonPanel = new JPanel();\n\t\tJButton savebutton = new JButton();\n\n\t\tsavebutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Save16.gif\"));\n\t\tResourceHelper.setText(savebutton, \"Save\");\n\t\tsavebutton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tsaveProject();\n\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(savebutton, savebutton.getName());\n\n\t\tGridBagConstraints bc = GridBagConstraintsFactory.create(0, 7,\n\t\t\t\tGridBagConstraints.BOTH, 0.0, 0.0);\n\t\tbc.gridwidth = 6;\n\t\tprojectInfoPanel.add(buttonPanel, bc);\n\n\t\t/*\n\t\t * link panel\n\t\t */\n\n\t\tlinkPanel.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"links\")));\n\t\tadd(linkPanel,\n\t\t\t\tGridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH));\n\n\t\t/*\n\t\t * panel that will contain the project's task list\n\t\t */\n\t\ttaskBorder = new JPanel();\n\t\ttaskBorder.setBorder(new TitledBorder(Resource\n\t\t\t\t.getResourceString(\"tasks\")));\n\t\ttaskBorder.setLayout(new GridBagLayout());\n\n\t\tadd(taskBorder, GridBagConstraintsFactory.create(0, 3,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.DockableView#refresh()\n\t */\n\t@Override\n\tpublic void refresh() {\n\t\t// the task editor does not refresh itself when the task data\n\t\t// model changes\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t\t\n\t\t// check if the item being edited was deleted\n\t\tif( event.getAction() == ChangeAction.DELETE && event.getObject() instanceof Project && ((Project)event.getObject()).getKey() == getShownId())\n\t\t\ttry {\n\t\t\t\tshowProject(Action.ADD, null, null);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t}\n\n\t/**\n\t * Save project.\n\t */\n\tprivate void saveProject() {\n\n\t\t// validate that description is present\n\t\tif (description.getText() == null || description.getText().trim().equals(\"\")) {\n\t\t\tErrmsg.getErrorHandler().notice(\n\t\t\t\t\tResource.getResourceString(\"empty_desc\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\n\t\t\tString num = projectIdText.getText();\n\n\t\t\tProject p = new Project();\n\n\t\t\tif (!num.equals(\"NEW\") && !num.equals(\"CLONE\")) {\n\t\t\t\tp.setKey(Integer.parseInt(num));\n\t\t\t\t\n\t\t\t\tProject indb = TaskModel.getReference().getProject(p.getKey());\n\t\t\t\tif( indb == null )\n\t\t\t\t{\n\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"project_not_found\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// fill in the fields from the screen\n\t\t\tCalendar cal = startDateChooser.getCalendar();\n\t\t\tif (cal == null)\n\t\t\t\tcal = new GregorianCalendar();\n\t\t\tp.setStartDate(cal.getTime()); // start date\n\n\t\t\tcal = dueDateChooser.getCalendar();\n\t\t\tif (cal != null) {\n\t\t\t\tp.setDueDate(cal.getTime()); // due date\n\n\t\t\t\t// validate due date\n\t\t\t\tif (DateUtil.isAfter(p.getStartDate(), p.getDueDate())) {\n\t\t\t\t\tthrow new Warning(Resource.getResourceString(\"sd_dd_warn\"));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tp.setDescription(description.getText());\n\t\t\tp.setStatus((String) statusComboBox.getSelectedItem());\n\n\t\t\tString cat = (String) categoryBox.getSelectedItem();\n\t\t\tif (cat.equals(\"\") || cat.equals(CategoryModel.UNCATEGORIZED)) {\n\t\t\t\tp.setCategory(null);\n\t\t\t} else {\n\t\t\t\tp.setCategory(cat);\n\t\t\t}\n\n\t\t\tp.setParent(null);\n\t\t\tString proj = (String) parentProjectComboBox.getSelectedItem();\n\t\t\ttry {\n\t\t\t\tp.setParent(getProjectId(proj));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// no project selected\n\t\t\t}\n\n\t\t\tTaskModel.getReference().saveProject(p);\n\t\t\tp.setKey(p.getKey());\n\n\t\t\tshowProject(Action.CHANGE, p, null);\n\t\t} catch (Warning w) {\n\t\t\tErrmsg.getErrorHandler().notice(w.getMessage());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Show a project\n\t * \n\t * @param function\n\t *            the action being taken\n\t * @param p\n\t *            the project\n\t * @param parentId\n\t *            the parent project id\n\t * \n\t * @throws Exception\n\t */\n\tprivate void showProject(Action function, Project p, Integer parentId)\n\t\t\tthrows Exception {\n\n\t\t// set the link panel to show this project's links\n\t\tlinkPanel.setOwner(p);\n\n\t\t// show all possible parent projects\n\t\tparentProjectComboBox.removeAllItems();\n\t\tparentProjectComboBox.addItem(\"\");\n\t\tCollection<Project> projects = TaskModel.getReference().getProjects();\n\t\tif (projects != null) {\n\t\t\tfor (Project project : projects) {\n\t\t\t\t// add open projects that are not the current one\n\t\t\t\tif ((p == null || p.getKey() != project.getKey())\n\t\t\t\t\t\t&& project.getStatus().equals(\n\t\t\t\t\t\t\t\tResource.getResourceString(\"OPEN\")))\n\t\t\t\t\tparentProjectComboBox.addItem(getProjectString(project));\n\t\t\t}\n\t\t}\n\n\t\t// if we are showing an existing project - fill in the gui fields for it\n\t\tif (p != null) {\n\t\t\t// task number\n\t\t\tprojectIdText.setText(Integer.toString(p.getKey()));\n\t\t\tprojectIdText.setEditable(false);\n\n\t\t\t// window title - \"Item N\"\n\t\t\twindowTitle = Resource.getResourceString(\"Item_\") + \" \"\n\t\t\t\t\t+ p.getKey();\n\n\t\t\t// due date\n\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\tDate dd = p.getDueDate();\n\t\t\tif (dd != null) {\n\t\t\t\tgc.setTime(dd);\n\t\t\t\tdueDateChooser.setCalendar(gc);\n\t\t\t}\n\n\t\t\tGregorianCalendar gc2 = new GregorianCalendar();\n\t\t\tdd = p.getStartDate();\n\t\t\tif (dd != null)\n\t\t\t\tgc2.setTime(dd);\n\t\t\tstartDateChooser.setCalendar(gc2);\n\n\t\t\tint daysleft = DateUtil.daysLeft(p.getDueDate());\n\t\t\tdaysLeftText.setText(Integer.toString(daysleft));\n\n\t\t\tString cat = p.getCategory();\n\t\t\tif (cat != null && !cat.equals(\"\")) {\n\t\t\t\tcategoryBox.setSelectedItem(cat);\n\t\t\t} else {\n\t\t\t\tcategoryBox.setSelectedIndex(0);\n\t\t\t}\n\n\t\t\tdescription.setText(p.getDescription());\n\n\t\t\tstatusComboBox.setEditable(false);\n\n\t\t\tCollection<Task> ptasks = TaskModel.getReference().getTasks(\n\t\t\t\t\tp.getKey());\n\t\t\ttotalTaskCount.setText(Integer.toString(ptasks.size()));\n\n\t\t\tint openTasks = 0;\n\t\t\tfor (Task pt : ptasks) {\n\t\t\t\tif (!TaskModel.isClosed(pt)) {\n\t\t\t\t\topenTasks++;\n\t\t\t\t}\n\t\t\t}\n\t\t\topenTaskCount.setText(Integer.toString(openTasks));\n\n\t\t\t// set parent project\n\t\t\tInteger pid = p.getParent();\n\t\t\tif (pid != null) {\n\t\t\t\tProject par = TaskModel.getReference().getProject(\n\t\t\t\t\t\tpid.intValue());\n\t\t\t\tif (TaskModel.isClosed(par)) {\n\t\t\t\t\t// if parent closed - would not have been added before\n\t\t\t\t\tparentProjectComboBox.addItem(getProjectString(par));\n\t\t\t\t}\n\t\t\t\tparentProjectComboBox.setSelectedItem(getProjectString(par));\n\n\t\t\t}\n\n\t\t\t// add the task list\n\t\t\tif (taskPanel == null) {\n\t\t\t\ttaskPanel = new TaskListPanel(TaskView.getProjectString(p));\n\t\t\t\ttaskPanel.addClosedTaskFilter();\n\t\t\t\ttaskBorder.add(taskPanel, GridBagConstraintsFactory.create(0,\n\t\t\t\t\t\t0, GridBagConstraints.BOTH, 1.0, 1.0));\n\t\t\t}\n\n\t\t} else {\n\t\t\t// set fields for a new project\n\n\t\t\tprojectIdText.setText(\"NEW\");\n\t\t\tprojectIdText.setEditable(false);\n\n\t\t\t// title\n\t\t\twindowTitle = Resource.getResourceString(\"NEW_Item\");\n\t\t\tstatusComboBox.addItem(Resource.getResourceString(\"OPEN\"));\n\t\t\tstatusComboBox.setEnabled(false);\n\t\t\tcategoryBox.setSelectedIndex(0);\n\t\t\tdescription.setText(\"\"); // desc\n\t\t\ttotalTaskCount.setText(\"\");\n\t\t\topenTaskCount.setText(\"\");\n\n\t\t\t// parent id may have been passed in\n\t\t\tif (parentId != null) {\n\t\t\t\tProject par = TaskModel.getReference().getProject(\n\t\t\t\t\t\tparentId.intValue());\n\t\t\t\tif (TaskModel.isClosed(par)) {\n\t\t\t\t\tparentProjectComboBox.addItem(getProjectString(par));\n\t\t\t\t}\n\t\t\t\tparentProjectComboBox.setSelectedItem(getProjectString(par));\n\n\t\t\t\tString cat = par.getCategory();\n\t\t\t\tif (cat != null && !cat.equals(\"\")) {\n\t\t\t\t\tcategoryBox.setSelectedItem(cat);\n\t\t\t\t} else {\n\t\t\t\t\tcategoryBox.setSelectedIndex(0);\n\t\t\t\t}\n\n\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\tDate dd = par.getDueDate();\n\t\t\t\tif (dd != null) {\n\t\t\t\t\tgc.setTime(dd);\n\t\t\t\t\tdueDateChooser.setCalendar(gc);\n\t\t\t\t}\n\n\t\t\t\tDate sd = par.getStartDate();\n\t\t\t\tif (sd != null) {\n\t\t\t\t\tgc.setTime(sd);\n\t\t\t\t\tstartDateChooser.setCalendar(gc);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// can't change status on a new project\n\t\tif (p == null) {\n\t\t\tstatusComboBox.setEnabled(false);\n\t\t}\n\n\t\t// cloning takes the fields filled in for an existing task and resets\n\t\t// only those\n\t\t// that don't apply to the clone\n\t\tif (function == Action.CLONE) {\n\t\t\t// need new task number\n\t\t\tprojectIdText.setText(\"CLONE\");\n\t\t\tprojectIdText.setEditable(false);\n\n\t\t\tstatusComboBox.removeAllItems();\n\t\t\tstatusComboBox.addItem(Resource.getResourceString(\"OPEN\"));\n\t\t\tstatusComboBox.setEnabled(false);\n\n\t\t}\n\t\t// change existing task\n\t\telse if (function == Action.CHANGE) {\n\n\t\t\tString state = null;\n\t\t\tif (p != null)\n\t\t\t\tstate = p.getStatus();\n\n\t\t\t// set next state pulldown - projects only move between open and\n\t\t\t// closed\n\t\t\tstatusComboBox.removeAllItems();\n\t\t\tstatusComboBox.addItem(Resource.getResourceString(\"OPEN\"));\n\t\t\tstatusComboBox.addItem(Resource.getResourceString(\"CLOSED\"));\n\t\t\tstatusComboBox.setSelectedItem(state);\n\t\t\tstatusComboBox.setEnabled(true);\n\n\t\t}\n\n\t}\n\t\n\tpublic int getShownId()\n\t{\n\t\tString num = projectIdText.getText();\n\t\tif (!num.equals(\"NEW\") && !num.equals(\"CLONE\")) \n\t\t\treturn Integer.parseInt(num);\n\t\t\t\n\t\treturn -1;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/TaskConfigurator.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2004 by Mike Berger\n */\npackage net.sf.borg.ui.task;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.TaskTypes;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.View;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\n\nimport javax.swing.*;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.event.ListSelectionListener;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Collection;\nimport java.util.Vector;\n\n/**\n * UI for editing task types, their state transitions, and pre-defined subtasks\n */\npublic class TaskConfigurator extends View {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** size of the task config window */\n\tstatic private final PrefName TASKCONFVIEWSIZE = new PrefName(\"taskconfviewsize\", \"-1,-1,-1,-1,N\");\n\n\t/**\n\t * Renders the initial task state in a different color\n\t */\n\tprivate class TypeListRenderer extends JLabel implements ListCellRenderer<Object> {\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tpublic TypeListRenderer() {\n\t\t\tsetOpaque(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,\n\t\t\t\tboolean cellHasFocus) {\n\t\t\tsetText(value.toString());\n\n\t\t\t// get initial state for the selected type\n\t\t\tString init = \"\";\n\t\t\tif (typeList.getSelectedValue() != null) {\n\t\t\t\tinit = taskTypes.getInitialState(typeList.getSelectedValue());\n\t\t\t}\n\n\t\t\t// highlist this state if it is the initial state\n\t\t\tif (value.toString().equals(init)) {\n\t\t\t\tsetBackground(isSelected ? Color.red : Color.white);\n\t\t\t\tsetForeground(isSelected ? Color.white : Color.red);\n\t\t\t} else {\n\t\t\t\t// return default component\n\t\t\t\treturn defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t/** The default table cell Renderer */\n\tstatic private final ListCellRenderer<Object> defaultRenderer = new DefaultListCellRenderer();\n\n\t/** The singleton */\n\tprivate static TaskConfigurator singleton = null;\n\n\t/**\n\t * Gets the singleton.\n\t * \n\t * @return the singleton\n\t * \n\t * @throws Exception\n\t */\n\tpublic static TaskConfigurator getReference() throws Exception {\n\t\tif (singleton == null || !singleton.isShowing())\n\t\t\tsingleton = new TaskConfigurator();\n\t\treturn (singleton);\n\t}\n\n\n\t/** The next state list. */\n\tprivate JList<String> nextStateList = null;\n\n\t/** The next state menu. */\n\tprivate JPopupMenu nextStateMenu = null;\n\n\t/** The state list. */\n\tprivate JList<String> stateList = null;\n\n\t/** The state menu. */\n\tprivate JPopupMenu stateMenu = null;\n\n\t/** The task types. */\n\tprivate final TaskTypes taskTypes;\n\n\t/** The type list. */\n\tprivate JList<String> typeList = null;\n\n\t/** The typeMenu. */\n\tprivate JPopupMenu typeMenu = null;\n\n\t/**\n\t * constructor\n\t * \n\t * @throws Exception\n\t */\n\tprivate TaskConfigurator() throws Exception {\n\t\tsuper();\n\n\t\tinitialize();\n\n\t\ttaskTypes = TaskModel.getReference().getTaskTypes().copy();\n\n\t\trefresh();\n\n\t\tmanageMySize(TASKCONFVIEWSIZE);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.View#destroy()\n\t */\n\t@Override\n\tpublic void destroy() {\n\t\tthis.dispose();\n\n\t}\n\n\n\t/**\n\t * Gets the next state menu.\n\t * \n\t * @return the next state menu\n\t */\n\tprivate JPopupMenu getNextStateMenu() {\n\t\tif (nextStateMenu == null) {\n\t\t\tnextStateMenu = new JPopupMenu();\n\n\t\t\tJMenuItem addItem = new JMenuItem();\n\t\t\taddItem.setText(net.sf.borg.common.Resource.getResourceString(\"Add\"));\n\t\t\taddItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (stateList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_state\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// prompt for selection of next state from a list\n\t\t\t\t\tCollection<String> states = taskTypes.getStates(typeList.getSelectedValue());\n\t\t\t\t\tObject[] sarray = states.toArray();\n\t\t\t\t\tString ns = (String) JOptionPane.showInputDialog(null,\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Select_next_state\"),\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Select_next_state\"),\n\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE, null, sarray, sarray[0]);\n\t\t\t\t\tif (ns == null)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t// add next state\n\t\t\t\t\ttaskTypes.addNextState(typeList.getSelectedValue(), stateList.getSelectedValue(), ns);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tnextStateMenu.add(addItem);\n\n\t\t\tJMenuItem deleteItem = new JMenuItem();\n\t\t\tdeleteItem.setText(net.sf.borg.common.Resource.getResourceString(\"Delete\"));\n\t\t\tdeleteItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (nextStateList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_state\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// delete next state\n\t\t\t\t\tString ns = nextStateList.getSelectedValue();\n\t\t\t\t\ttaskTypes.deleteNextState(typeList.getSelectedValue(), stateList.getSelectedValue(), ns);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tnextStateMenu.add(deleteItem);\n\t\t}\n\t\treturn nextStateMenu;\n\t}\n\n\t/**\n\t * Gets the state menu.\n\t * \n\t * @return the state menu\n\t */\n\tprivate JPopupMenu getStateMenu() {\n\t\tif (stateMenu == null) {\n\t\t\tstateMenu = new JPopupMenu();\n\n\t\t\tJMenuItem addItem = new JMenuItem();\n\t\t\taddItem.setText(net.sf.borg.common.Resource.getResourceString(\"Add_State\"));\n\t\t\taddItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (typeList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_type\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tString newstate = JOptionPane\n\t\t\t\t\t\t\t.showInputDialog(net.sf.borg.common.Resource.getResourceString(\"New_State\"));\n\t\t\t\t\tif (newstate == null)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t// add a new state\n\t\t\t\t\ttaskTypes.addState(typeList.getSelectedValue(), newstate);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tstateMenu.add(addItem);\n\n\t\t\tJMenuItem renameItem = new JMenuItem();\n\t\t\trenameItem.setText(net.sf.borg.common.Resource.getResourceString(\"Rename_State\"));\n\t\t\trenameItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (stateList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_state\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tString newstate = JOptionPane\n\t\t\t\t\t\t\t.showInputDialog(net.sf.borg.common.Resource.getResourceString(\"New_State\"));\n\t\t\t\t\tif (newstate == null)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t// rename state\n\t\t\t\t\ttaskTypes.changeState(typeList.getSelectedValue(), stateList.getSelectedValue(), newstate);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tstateMenu.add(renameItem);\n\n\t\t\tJMenuItem deleteItem = new JMenuItem();\n\t\t\tdeleteItem.setText(net.sf.borg.common.Resource.getResourceString(\"Delete_State\"));\n\t\t\tdeleteItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (stateList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_state\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// confirm delete\n\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Really_Delete_\") + \" \"\n\t\t\t\t\t\t\t\t\t+ stateList.getSelectedValue(),\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Confirm_Delete\"),\n\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t// delete state\n\t\t\t\t\ttaskTypes.deleteState(typeList.getSelectedValue(), stateList.getSelectedValue());\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tstateMenu.add(deleteItem);\n\t\t}\n\t\treturn stateMenu;\n\t}\n\n\t/**\n\t * Gets the type menu.\n\t * \n\t * @return the type menu\n\t */\n\tprivate JPopupMenu getTypeMenu() {\n\t\tif (typeMenu == null) {\n\t\t\ttypeMenu = new JPopupMenu();\n\n\t\t\tJMenuItem addItem = new JMenuItem();\n\t\t\taddItem.setText(net.sf.borg.common.Resource.getResourceString(\"Add_Type\"));\n\t\t\taddItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tString newtype = JOptionPane\n\t\t\t\t\t\t\t.showInputDialog(net.sf.borg.common.Resource.getResourceString(\"New_Task_Type\"));\n\t\t\t\t\tif (newtype == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t// add a new type\n\t\t\t\t\ttaskTypes.addType(newtype);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttypeMenu.add(addItem);\n\n\t\t\tJMenuItem renameItem = new JMenuItem();\n\t\t\trenameItem.setText(net.sf.borg.common.Resource.getResourceString(\"Rename_Type\"));\n\t\t\trenameItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (typeList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_type\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tboolean exists = false;\n\n\t\t\t\t\t// check for existing tasks\n\t\t\t\t\ttry {\n\t\t\t\t\t\tCollection<Task> tasks = TaskModel.getReference().getTasksByType(typeList.getSelectedValue());\n\t\t\t\t\t\tif (tasks != null && !tasks.isEmpty()) {\n\n\t\t\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"rename_task_warning\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[] { typeList.getSelectedValue() }),\n\t\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Confirm\"),\n\t\t\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\texists = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tString newtype = JOptionPane\n\t\t\t\t\t\t\t.showInputDialog(net.sf.borg.common.Resource.getResourceString(\"New_Task_Type\"));\n\t\t\t\t\tif (newtype == null)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\t\n\t\t\t\t\t\tif( exists )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tTaskModel.getReference().renameTaskType(typeList.getSelectedValue(), newtype);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// rename type\n\t\t\t\t\t\ttaskTypes.changeType(typeList.getSelectedValue(), newtype);\n\t\t\t\t\t\tTaskModel.getReference().saveTaskTypes(taskTypes);\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t} catch (Exception e2) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e2);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttypeMenu.add(renameItem);\n\n\t\t\tJMenuItem deleteItem = new JMenuItem();\n\t\t\tdeleteItem.setText(net.sf.borg.common.Resource.getResourceString(\"Delete_Type\"));\n\t\t\tdeleteItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (typeList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_type\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// check for existing tasks\n\t\t\t\t\ttry {\n\t\t\t\t\t\tCollection<Task> tasks = TaskModel.getReference().getTasksByType(typeList.getSelectedValue());\n\t\t\t\t\t\tif (tasks != null && !tasks.isEmpty()) {\n\n\t\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"del_task_warning\",\n\t\t\t\t\t\t\t\t\tnew Object[] { typeList.getSelectedValue() }));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// confirm delete\n\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Really_Delete_\") + \" \"\n\t\t\t\t\t\t\t\t\t+ typeList.getSelectedValue(),\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Confirm_Delete\"),\n\t\t\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t// delete type\n\t\t\t\t\ttaskTypes.deleteType(typeList.getSelectedValue());\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttypeMenu.add(deleteItem);\n\n\t\t\tJMenuItem initialStateItem = new JMenuItem();\n\t\t\tinitialStateItem.setText(net.sf.borg.common.Resource.getResourceString(\"Set_Initial_State\"));\n\t\t\tinitialStateItem.addActionListener(new ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\tif (typeList.getSelectedIndex() < 0) {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\n\t\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Please_select_a_type\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// prompt for selection of initial state from a list\n\t\t\t\t\tCollection<String> states = taskTypes.getStates(typeList.getSelectedValue());\n\t\t\t\t\tObject[] sarray = states.toArray();\n\t\t\t\t\tString ns = (String) JOptionPane.showInputDialog(null,\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Select_initial_state\"),\n\t\t\t\t\t\t\tnet.sf.borg.common.Resource.getResourceString(\"Select_initial_state\"),\n\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE, null, sarray, sarray[0]);\n\t\t\t\t\tif (ns == null)\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t// set initial state\n\t\t\t\t\ttaskTypes.setInitialState(typeList.getSelectedValue(), ns);\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttypeMenu.add(initialStateItem);\n\t\t}\n\t\treturn typeMenu;\n\t}\n\n\t/**\n\t * initialize the UI\n\t * \n\t */\n\tprivate void initialize() {\n\n\t\tthis.setTitle(net.sf.borg.common.Resource.getResourceString(\"Task_State_Editor\"));\n\t\tthis.setSize(564, 219);\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\n\t\tGridLayout gridLayout = new GridLayout();\n\t\tgridLayout.setRows(1);\n\n\t\t/*\n\t\t * list containing panel\n\t\t */\n\t\tJPanel listContainerPanel = new JPanel();\n\t\tlistContainerPanel.setLayout(gridLayout);\n\n\t\tGridLayout gridLayout9 = new GridLayout();\n\n\t\t/*\n\t\t * task type panel\n\t\t */\n\t\tJPanel taskTypePanel = new JPanel();\n\t\ttaskTypePanel.setLayout(gridLayout9);\n\t\ttaskTypePanel.setBorder(\n\t\t\t\tBorderFactory.createTitledBorder(null, net.sf.borg.common.Resource.getResourceString(\"Task_Types\"),\n\t\t\t\t\t\tTitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));\n\t\tgridLayout9.setRows(1);\n\n\t\tJScrollPane taskTypeScroll = new JScrollPane();\n\n\t\ttypeList = new JList<String>();\n\t\ttypeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\t\ttypeList.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tif (e.getButton() == MouseEvent.BUTTON3) {\n\t\t\t\t\tgetTypeMenu().show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttypeList.addListSelectionListener(new ListSelectionListener() {\n\t\t\t@Override\n\t\t\tpublic void valueChanged(ListSelectionEvent e) {\n\t\t\t\ttypeSelectHandler();\n\t\t\t}\n\t\t});\n\n\t\ttaskTypeScroll.setViewportView(typeList);\n\t\ttaskTypeScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n\t\ttaskTypeScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\n\n\t\ttaskTypePanel.add(taskTypeScroll, null);\n\n\t\tlistContainerPanel.add(taskTypePanel, null);\n\n\t\t/*\n\t\t * task states panel\n\t\t */\n\t\tGridLayout gridLayout10 = new GridLayout();\n\t\tJPanel taskStatesPanel = new JPanel();\n\t\ttaskStatesPanel.setLayout(gridLayout10);\n\t\ttaskStatesPanel.setBorder(\n\t\t\t\tBorderFactory.createTitledBorder(null, net.sf.borg.common.Resource.getResourceString(\"States\"),\n\t\t\t\t\t\tTitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));\n\t\tgridLayout10.setRows(1);\n\n\t\tJScrollPane stateScroll = new JScrollPane();\n\n\t\tstateList = new JList<String>();\n\t\tstateList.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tif (e.getButton() == MouseEvent.BUTTON3) {\n\t\t\t\t\tgetStateMenu().show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tstateList.addListSelectionListener(new ListSelectionListener() {\n\t\t\t@Override\n\t\t\tpublic void valueChanged(ListSelectionEvent e) {\n\t\t\t\tstateSelectHandler();\n\t\t\t}\n\t\t});\n\t\tstateList.setCellRenderer(new TypeListRenderer());\n\n\t\tstateScroll.setViewportView(stateList);\n\t\tstateScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n\t\tstateScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\n\n\t\ttaskStatesPanel.add(stateScroll, null);\n\n\t\tlistContainerPanel.add(taskStatesPanel, null);\n\n\t\t/*\n\t\t * next state panel\n\t\t */\n\t\tGridLayout gridLayout8 = new GridLayout();\n\t\tJPanel nextStatePanel = new JPanel();\n\t\tnextStatePanel.setLayout(gridLayout8);\n\t\tnextStatePanel.setBorder(\n\t\t\t\tBorderFactory.createTitledBorder(null, net.sf.borg.common.Resource.getResourceString(\"Next_States\"),\n\t\t\t\t\t\tTitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));\n\t\tgridLayout8.setRows(1);\n\n\t\tJScrollPane nextStateScroll = new JScrollPane();\n\n\t\tnextStateList = new JList<String>();\n\t\tnextStateList.addMouseListener(new MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tif (e.getButton() == MouseEvent.BUTTON3) {\n\t\t\t\t\tgetNextStateMenu().show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tnextStateScroll.setViewportView(nextStateList);\n\t\tnextStateScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);\n\t\tnextStateScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n\n\t\tnextStatePanel.add(nextStateScroll, null);\n\n\t\tlistContainerPanel.add(nextStatePanel, null);\n\n\t\ttopPanel.add(listContainerPanel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * button panel\n\t\t */\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.setLayout(new FlowLayout());\n\n\t\tJButton saveButton = new JButton();\n\t\tResourceHelper.setText(saveButton, \"Save\");\n\t\tsaveButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tTaskModel.getReference().saveTaskTypes(taskTypes);\n\t\t\t\t\tTaskConfigurator.getReference().dispose();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(saveButton, null);\n\n\t\tJButton dismissButton = new JButton();\n\t\tResourceHelper.setText(dismissButton, \"Dismiss\");\n\t\tdismissButton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tTaskConfigurator.getReference().dispose();\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t// empty\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tsetDismissButton(dismissButton);\n\n\t\tbuttonPanel.add(dismissButton, null);\n\n\t\ttopPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.NONE, 0.0, 0.0));\n\n\t\tthis.setContentPane(topPanel);\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.View#refresh()\n\t */\n\t@Override\n\tpublic void refresh() {\n\t\t/*\n\t\t * reload the task types from the model reset the selected type if the\n\t\t * user had one selected this selection triggers further UI updates\n\t\t */\n\t\tObject typesel = typeList.getSelectedValue();\n\t\tVector<String> types = taskTypes.getTaskTypes();\n\t\ttypeList.setListData(types);\n\t\tif (typesel != null)\n\t\t\ttypeList.setSelectedValue(typesel, true);\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * display next states when user selects a state\n\t */\n\tprivate void stateSelectHandler() {\n\t\tString type = typeList.getSelectedValue();\n\t\tString state = stateList.getSelectedValue();\n\t\tif (state == null)\n\t\t\treturn;\n\t\tCollection<String> states = taskTypes.nextStates(type, state);\n\t\tstates.remove(state);\n\t\tnextStateList.setListData(states.toArray(new String[0]));\n\t}\n\n\t/**\n\t * when the user selected a type, display its states, next states, and\n\t * subtasks\n\t */\n\tprivate void typeSelectHandler() {\n\t\tString type = typeList.getSelectedValue();\n\t\tif (type == null)\n\t\t\treturn;\n\t\tCollection<String> states = taskTypes.getStates(type);\n\t\tstateList.setListData(states.toArray(new String[0]));\n\t\tnextStateList.setListData(new Vector<String>());\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/TaskFilterPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.task;\n\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.util.Collection;\nimport java.util.TreeSet;\nimport java.util.Vector;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextField;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.TaskTypes;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * Wraps a Task List Table with filter criteria. This is the UI for the Tasks\n * sub-tab under the main Tasks tab\n */\npublic class TaskFilterPanel extends JPanel {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The case sensitive box. */\n\tprivate JCheckBox caseSensitiveBox = null;\n\n\t/** The filter string. */\n\tprivate JTextField filterString;\n\n\t/** The project selector. */\n\tprivate JComboBox<String> projectSelector = null;\n\n\t/** The status selector. */\n\tprivate JComboBox<String> statusSelector = null;\n\n\t/** The task list. */\n\tprivate TaskListPanel taskList = null;\n\n\t/**\n\t * Instantiates a new task filter panel.\n\t */\n\tpublic TaskFilterPanel() {\n\t\tsuper();\n\n\t\ttry {\n\t\t\tinitComponents();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\t\t\n\t}\n\n\t/**\n\t * initialize the UI\n\t * @throws Exception\n\t */\n\tprivate void initComponents() throws Exception {\n\n\t\tJButton filterButton = new JButton();\n\t\tfilterString = new javax.swing.JTextField();\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\tFlowLayout flowLayout = new FlowLayout();\n\t\tflowLayout.setAlignment(java.awt.FlowLayout.LEFT);\n\n\t\tJPanel comboBoxPanel = new JPanel();\n\t\tcomboBoxPanel.setLayout(flowLayout);\n\n\t\tJLabel statusLabel = new JLabel();\n\t\tstatusLabel.setText(Resource.getResourceString(\"Status\") + \":\");\n\t\tcomboBoxPanel.add(statusLabel, null);\n\n\t\tstatusSelector = new JComboBox<String>();\n\t\tsetStatuses();\n\t\tstatusSelector\n\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(\n\t\t\t\t\t\t\tjava.awt.event.ActionEvent evt) {\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tcomboBoxPanel.add(statusSelector, null);\n\n\t\tJLabel projectLabel = new JLabel(Resource.getResourceString(\"project\")\n\t\t\t\t+ \":\");\n\t\tJLabel spacer = new JLabel(\"           \");\n\t\tcomboBoxPanel.add(spacer, null);\n\t\tcomboBoxPanel.add(projectLabel, null);\n\t\t\n\t\tprojectSelector = new JComboBox<String>();\n\t\tloadProjectBox();\n\t\tprojectSelector\n\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(\n\t\t\t\t\t\t\tjava.awt.event.ActionEvent evt) {\n\t\t\t\t\t\trefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tcomboBoxPanel.add(projectSelector);\n\n\t\tGridBagConstraints gridBagConstraints15 = GridBagConstraintsFactory\n\t\t\t\t.create(0, 0, GridBagConstraints.HORIZONTAL);\n\t\tgridBagConstraints15.gridwidth = 3;\n\t\tthis.add(comboBoxPanel, gridBagConstraints15);\n\n\t\tfilterButton.setIcon(IconHelper.getIcon(\n\t\t\t\t\"/resource/Find16.gif\"));\n\t\tResourceHelper.setText(filterButton, \"Filter:\");\n\t\tfilterButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\trefresh();\n\n\t\t\t}\n\t\t});\n\t\tthis.add(filterButton, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.HORIZONTAL));\n\t\tthis.add(filterString, GridBagConstraintsFactory.create(1, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\t\t\n\t\tcaseSensitiveBox = new JCheckBox();\n\t\tcaseSensitiveBox.setText(Resource\n\t\t\t\t.getResourceString(\"case_sensitive\"));\n\t\tthis.add(caseSensitiveBox, GridBagConstraintsFactory.create(2, 1));\n\n\t\ttaskList = new TaskListPanel();\n\t\tJScrollPane taskListScroll = new JScrollPane();\n\t\ttaskListScroll.setViewportView(taskList);\n\t\tGridBagConstraints gridBagConstraints11 = GridBagConstraintsFactory\n\t\t\t\t.create(0, 2, GridBagConstraints.BOTH, 1.0, 1.0);\n\t\tgridBagConstraints11.gridwidth = 3;\n\t\tthis.add(taskListScroll, gridBagConstraints11);\n\n\t\trefresh();\n\n\t}\n\n\t/**\n\t * Load the project box with the list of projects and an entry to match All projects\n\t * \n\t * @throws Exception the exception\n\t */\n\tprivate void loadProjectBox() throws Exception {\n\t\tprojectSelector.removeAllItems();\n\t\tprojectSelector.addItem(Resource.getResourceString(\"All\"));\n\t\ttry {\n\t\t\tCollection<Project> projects = TaskModel.getReference()\n\t\t\t\t\t.getProjects();\n\t\t\tfor(Project p : projects) {\n\t\t\t\tprojectSelector.addItem(TaskView.getProjectString(p));\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t  // empty\n\t\t}\n\t}\n\n\t/**\n\t * Prints the task list\n\t */\n\tpublic void print() {\n\n\t\t// print the current table of tasks\n\t\ttry {\n\t\t\ttaskList.print();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\n\t/**\n\t * Update the task list based on changes to filter criteria\n\t */\n\tpublic void refresh() {\n\n\t\t// reload project filter\n\t\tObject o = projectSelector.getSelectedItem();\n\t\ttry {\n\t\t\tloadProjectBox();\n\t\t} catch (Exception e1) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\treturn;\n\t\t}\n\t\tif (o != null)\n\t\t\tprojectSelector.setSelectedItem(o);\n\n\t\t// get any filter string the user has typed\n\t\tString filt = filterString.getText();\n\n\t\tString statfilt = (String) statusSelector.getSelectedItem();\n\n\t\tString projfilt = (String) projectSelector.getSelectedItem();\n\n\t\t// pass the filter criteria to the task list. It has the filtering\n\t\t// capabilities. This panel and the task list used to be one class\n\t\t// and when they were split, the filtering was needed in the task list\n\t\t// class for other reasons\n\t\ttaskList.setFilterCriteria(projfilt, statfilt, filt, caseSensitiveBox\n\t\t\t\t.isSelected());\n\t\ttaskList.refresh();\n\n\t}\n\n\t/**\n\t * load the status combo box\n\t * \n\t */\n\tprivate void setStatuses() {\n\n\t\t// add some wildcard-like statuses\n\t\tstatusSelector.addItem(Resource.getResourceString(\"All_Open\"));\n\t\tstatusSelector.addItem(Resource.getResourceString(\"All\"));\n\t\t\n\t\t// get all possible statuses\n\t\tTaskTypes t = TaskModel.getReference().getTaskTypes();\n\t\tTreeSet<String> statusSet = new TreeSet<String>();\n\t\tVector<String> types = t.getTaskTypes();\n\t\tfor(String taskType : types ) {\n\t\t\t// create a list of statuses in a Set to remove duplicates\n\t\t\tCollection<String> states = t.getStates(taskType);\n\t\t\tfor( String status : states ) {\n\t\t\t\tstatusSet.add(status);\n\t\t\t}\n\t\t}\n\t\tfor(String status : statusSet) {\n\t\t\tstatusSelector.addItem(status);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/TaskListPanel.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.task;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.FlowLayout;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.Collection;\nimport java.util.Date;\n\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTable;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.event.TableModelEvent;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.Model;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.TaskTypes;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TablePrinter;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * Shows a table of tasks action buttons.\n */\nclass TaskListPanel extends JPanel implements Model.Listener {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\n\t/** The button panel. */\n\tprivate JPanel buttonPanel = null;\n\n\t/** checkbox to filter closed tasks */\n\tprivate JCheckBox showClosedTasksCheckBox = null;\n\t\n\t/** checkbox to show subtasks */\n\tprivate JCheckBox showSubTasksBox = null;\n\n\tprivate JLabel totalLabel;\n\tprivate JLabel getTotalLabel() {\n\t\tif (totalLabel == null) {\n\t\t\ttotalLabel = new JLabel();\n\t\t}\n\t\treturn totalLabel;\n\t}\n\n\t/** The filter string. */\n\tprivate String filterString = \"\";\n\n\t/** The filter case sensitive flag. */\n\tprivate boolean filterCaseSensitive = false;\n\n\t// filtering criteria\n\t/** The project name. */\n\tprivate String projectName = Resource.getResourceString(\"All\");\n\n\t/** The task status. */\n\tprivate String taskStatus = Resource.getResourceString(\"All\");\n\n\t/** The task table. */\n\tprivate JTable taskTable;\n\n\t/**\n\t * constructor\n\t */\n\tpublic TaskListPanel() {\n\t\tsuper();\n\t\tTaskModel.getReference().addListener(this);\n\t\ttry {\n\t\t\tinitComponents();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t}\n\t\n\tpublic void cleanUp() {\n\t\tTaskModel.getReference().removeListener(this);\n\t}\n\n\t/**\n\t * Instantiates a new task list panel tied to a particular project\n\t * \n\t * @param projectName\n\t *            the project name\n\t */\n\tpublic TaskListPanel(String projectName) {\n\t\tsuper();\n\t\tsetFilterCriteria(projectName, Resource.getResourceString(\"All\"), \"\",\n\t\t\t\tfalse);\n\t\tTaskModel.getReference().addListener(this);\n\t\ttry {\n\t\t\tinitComponents();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\t\t\n\t}\n\n\t/**\n\t * get the selected task id\n\t * \n\t * @return the select task id or null\n\t */\n\tprivate Integer getSelectedTaskId() {\n\t\t// get task number from column 0 of selected row\n\t\tint row = taskTable.getSelectedRow();\n\t\tif (row == -1)\n\t\t\treturn null;\n\t\tTableSorter tm = (TableSorter) taskTable.getModel();\n\t\tInteger num = (Integer) tm.getValueAt(row, 0);\n\t\tif( num == null)\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"no_subtask_action\"));\n\t\treturn num;\n\t}\n\n\t/**\n\t * close the selected task\n\t * \n\t * */\n\tprivate void closeActionPerformed() {\n\n\t\tInteger num = getSelectedTaskId();\n\t\tif (num == null)\n\t\t\treturn;\n\t\ttry {\n\t\t\tTaskModel.getReference().close(num.intValue());\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * delete the selected task\n\t * \n\t */\n\tprivate void deleteActionPerformed() {\n\n\t\tInteger num = getSelectedTaskId();\n\t\tif (num == null)\n\t\t\treturn;\n\n\t\t// confirm delete\n\t\tint ret = JOptionPane.showConfirmDialog(null, Resource\n\t\t\t\t.getResourceString(\"Really_delete_number_\")\n\t\t\t\t+ \" \" + num, Resource.getResourceString(\"Confirm_Delete\"), JOptionPane.YES_NO_OPTION);\n\t\tif (ret == JOptionPane.YES_OPTION) {\n\t\t\t// delete the task\n\t\t\ttry {\n\t\t\t\t//remove all of the tasks instances from the multiView\n\t\t\t\tnet.sf.borg.ui.MultiView.getMainView().removeView(Resource.getResourceString(\"Item_\") + \" \" + TaskModel.getReference().getTask(num.intValue()).getKey());\n\t\t\t\t//delete task\n\t\t\t\tTaskModel.getReference().delete(num.intValue());\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t *create the button panel\n\t * \n\t * @return the button panel\n\t */\n\tprivate JPanel getButtonPanel() {\n\t\tif (buttonPanel == null) {\n\t\t\tbuttonPanel = new JPanel();\n\t\t\tbuttonPanel.setLayout(new FlowLayout());\n\n\t\t\tJButton addbutton = new JButton();\n\t\t\taddbutton.setText(Resource.getResourceString(\"Add\"));\n\t\t\taddbutton.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Add16.gif\"));\n\t\t\taddbutton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\ttask_add();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(addbutton, null);\n\n\t\t\tJButton changebutton1 = new JButton();\n\t\t\tchangebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Edit16.gif\"));\n\t\t\tchangebutton1.setText(Resource.getResourceString(\"Change\"));\n\t\t\tchangebutton1\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tInteger num = getSelectedTaskId();\n\t\t\t\t\t\t\tif (num != null)\n\t\t\t\t\t\t\t\ttask_change(num.intValue());\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\tbuttonPanel.add(changebutton1, null);\n\n\t\t\tJButton deletebutton1 = new JButton();\n\t\t\tdeletebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Delete16.gif\"));\n\t\t\tdeletebutton1.setText(Resource.getResourceString(\"Delete\"));\n\t\t\tdeletebutton1\n\t\t\t\t\t.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t\t\tdeleteActionPerformed();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\tbuttonPanel.add(deletebutton1, null);\n\n\t\t\tJButton closebutton1 = new JButton();\n\t\t\tclosebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/greenlight.gif\"));\n\t\t\tclosebutton1.setText(Resource.getResourceString(\"Close\"));\n\t\t\tclosebutton1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tcloseActionPerformed();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(closebutton1, null);\n\n\t\t\tJButton clonebutton1 = new JButton();\n\t\t\tclonebutton1.setIcon(IconHelper.getIcon(\n\t\t\t\t\t\"/resource/Copy16.gif\"));\n\t\t\tclonebutton1.setText(Resource.getResourceString(\"Clone\"));\n\t\t\tclonebutton1.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tInteger num = getSelectedTaskId();\n\t\t\t\t\tif (num != null)\n\t\t\t\t\t\ttask_clone(num.intValue());\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(clonebutton1, null);\n\t\t\t\n\t\t\taddSubTaskFilter();\n\n\t\t}\n\t\treturn buttonPanel;\n\t}\n\t\n\t/**\n\t * add the closed tasks checkbox. would not be called when an external mechanism \n\t * is filtering by status in some other way\n\t */\n\tpublic void addClosedTaskFilter()\n\t{\n\t\tshowClosedTasksCheckBox = new JCheckBox();\n\t\tshowClosedTasksCheckBox.setText(Resource\n\t\t\t\t.getResourceString(\"show_closed_tasks\"));\n\t\tshowClosedTasksCheckBox.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\trefresh();\n\t\t\t}\n\n\t\t});\n\t\tbuttonPanel.add(showClosedTasksCheckBox);\n\t\trefresh();\n\t}\n\t\n\tprivate void addSubTaskFilter()\n\t{\n\t\tshowSubTasksBox = new JCheckBox();\n\t\tshowSubTasksBox.setText(Resource\n\t\t\t\t.getResourceString(\"show_subtasks\"));\n\t\tshowSubTasksBox.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\trefresh();\n\t\t\t}\n\n\t\t});\n\t\tbuttonPanel.add(showSubTasksBox);\n\t\trefresh();\n\t}\n\n\t/**\n\t * initalize the UI\n\t * \n\t * @throws Exception\n\t */\n\tprivate void initComponents() throws Exception {\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\t/*\n\t\t * task table\n\t\t */\n\t\ttaskTable = new JTable();\n\n\n\t\t// use a sorted table model\n\t\ttaskTable.setModel(new TableSorter(new String[] {\n\t\t\t\tResource.getResourceString(\"Item_#\"),\n\t\t\t\tResource.getResourceString(\"Status\"),\n\t\t\t\tResource.getResourceString(\"Type\"),\n\t\t\t\tResource.getResourceString(\"Pri\"),\n\t\t\t\tResource.getResourceString(\"Days_Left\"),\n\t\t\t\tResource.getResourceString(\"summary\"),\n\t\t\t\tResource.getResourceString(\"Start_Date\"),\n\t\t\t\tResource.getResourceString(\"Due_Date\"),\n\t\t\t\tResource.getResourceString(\"close_date\"),\n\t\t\t\tResource.getResourceString(\"duration\"),\n\t\t\t\tResource.getResourceString(\"elapsed_time\"),\n\t\t\t\tResource.getResourceString(\"project\"),\n\t\t\t\tResource.getResourceString(\"Category\") }, new Class[] {\n\t\t\t\tInteger.class, String.class, String.class, Integer.class,\n\t\t\t\tInteger.class, String.class, Date.class, Date.class, Date.class,\n\t\t\t\tInteger.class, Integer.class, String.class, String.class }));\n\n\t\t// set up for sorting when a column header is clicked\n\t\tTableSorter tm = (TableSorter) taskTable.getModel();\n\t\ttm.addMouseListenerToHeaderInTable(taskTable);\n\n\t\t// clear all rows\n\t\ttm.setRowCount(0);\n\t\ttm.tableChanged(new TableModelEvent(tm));\n\n\t\tJScrollPane taskScroll = new JScrollPane();\n\t\ttaskScroll.setViewportView(taskTable);\n\t\ttaskScroll.setBorder(javax.swing.BorderFactory\n\t\t\t\t.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));\n\t\ttaskTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);\n\t\ttaskTable.addMouseListener(new java.awt.event.MouseAdapter() {\n\t\t\t@Override\n\t\t\tpublic void mouseClicked(java.awt.event.MouseEvent evt) {\n\t\t\t\t// on double click - open task for edit\n\t\t\t\tif (evt.getClickCount() < 2)\n\t\t\t\t\treturn;\n\t\t\t\tInteger num = getSelectedTaskId();\n\t\t\t\tif (num != null)\n\t\t\t\t\ttask_change(num.intValue());\n\t\t\t}\n\t\t});\n\t\ttaskTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\n\t\t// set column widths\n\t\ttaskTable.getColumnModel().getColumn(8).setPreferredWidth(100);\n\t\ttaskTable.getColumnModel().getColumn(7).setPreferredWidth(100);\n\t\ttaskTable.getColumnModel().getColumn(6).setPreferredWidth(100);\n\t\ttaskTable.getColumnModel().getColumn(5).setPreferredWidth(400);\n\t\ttaskTable.setPreferredScrollableViewportSize(new Dimension(800, 200));\n\t\ttaskTable.setShowGrid(true);\n\t\ttaskTable.setIntercellSpacing(new Dimension(1, 1));\n\n\n\t\t/*\n\t\t * popup menu for task table\n\t\t */\n\t\tnew PopupMenuHelper(taskTable, new PopupMenuHelper.Entry[] {\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\ttask_add();\n\t\t\t\t\t}\n\t\t\t\t}, \"Add\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tInteger num = getSelectedTaskId();\n\t\t\t\t\t\tif (num != null)\n\t\t\t\t\t\t\ttask_change(num.intValue());\n\t\t\t\t\t}\n\t\t\t\t}, \"Change\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tInteger num = getSelectedTaskId();\n\t\t\t\t\t\tif (num != null)\n\t\t\t\t\t\t\ttask_clone(num.intValue());\n\t\t\t\t\t}\n\t\t\t\t}, \"Clone\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tdeleteActionPerformed();\n\t\t\t\t\t}\n\t\t\t\t}, \"Delete\"),\n\t\t\t\tnew PopupMenuHelper.Entry(new java.awt.event.ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\t\t\tcloseActionPerformed();\n\t\t\t\t\t}\n\t\t\t\t}, \"Close\") });\n\n\t\tthis.add(taskScroll, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * add button panel\n\t\t */\n\t\tJPanel midPanel = new JPanel(new BorderLayout());\n\t\tmidPanel.add(getTotalLabel(), BorderLayout.WEST);\n\t\tmidPanel.add(getButtonPanel(), BorderLayout.CENTER);\n\t\tthis.add(midPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH));\n\n\t\trefresh();\n\n\t}\n\t\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\t}\n\n\t/**\n\t * Prints the task table\n\t */\n\tpublic void print() {\n\n\t\ttry {\n\t\t\tTablePrinter.printTable(taskTable);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * reload from the model and re-apply filter criteria\n\t */\n\tpublic void refresh() {\n\n\n\t\t// clear all table rows\n\t\tTableSorter tm = (TableSorter) taskTable.getModel();\n\t\ttm.setRowCount(0);\n\t\ttm.tableChanged(new TableModelEvent(tm));\n\n\t\t// get project id to filter on\n\t\tInteger projfiltid = null;\n\t\tif (!projectName.equals(Resource.getResourceString(\"All\"))) {\n\t\t\ttry {\n\t\t\t\tprojfiltid = TaskView.getProjectId(projectName);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\tString subtaskString = Resource.getResourceString(\"subtask\");\n\n\t\ttry {\n\t\t\t// loop through all tasks\n\t\t\tTaskTypes tasktypes = TaskModel.getReference().getTaskTypes();\n\t\t\tCollection<Task> tasks = TaskModel.getReference().getTasks();\n\t\t\tint totalItems = 0;\n\t\t\tfor (Task task : tasks) {\n\n\t\t\t\t// filter by task state\n\t\t\t\tString st = task.getState();\n\t\t\t\tif (taskStatus.equals(Resource.getResourceString(\"All_Open\"))) {\n\t\t\t\t\tif (TaskModel.isClosed(task)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} else if (!taskStatus\n\t\t\t\t\t\t.equals(Resource.getResourceString(\"All\"))\n\t\t\t\t\t\t&& !taskStatus.equals(st))\n\t\t\t\t\tcontinue;\n\t\t\t\t\n\t\t\t\tif( showClosedTasksCheckBox != null && !showClosedTasksCheckBox.isSelected() && TaskModel.isClosed(task))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\n\t\t\t\t// filter by project\n\t\t\t\tInteger pid = task.getProject();\n\t\t\t\tif (projfiltid != null) {\n\t\t\t\t\tif (pid == null || pid.intValue() != projfiltid.intValue())\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// filter by category\n\t\t\t\tif (!CategoryModel.getReference().isShown(task.getCategory()))\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// filter on user filter string\n\t\t\t\tif (filterString.length() != 0) {\n\n\t\t\t\t\t// check if string is in description\n\t\t\t\t\t// or resolution\n\t\t\t\t\tString s = task.getSummary();\n\t\t\t\t\tString d = task.getDescription();\n\t\t\t\t\tString r = task.getResolution();\n\n\t\t\t\t\tif (r == null)\n\t\t\t\t\t\tr = \"\";\n\t\t\t\t\tif (s == null)\n\t\t\t\t\t\ts = \"\";\n\t\t\t\t\tif (d == null)\n\t\t\t\t\t\td = \"\";\n\n\t\t\t\t\tif (filterCaseSensitive) {\n\t\t\t\t\t\tif (d.indexOf(filterString) == -1\n\t\t\t\t\t\t\t\t&& r.indexOf(filterString) == -1 &&\n\t\t\t\t\t\t\t\ts.indexOf(filterString) == -1)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString lfilt = filterString.toLowerCase();\n\t\t\t\t\t\tString ld = d.toLowerCase();\n\t\t\t\t\t\tString lr = r.toLowerCase();\n\t\t\t\t\t\tString ls = s.toLowerCase();\n\t\t\t\t\t\tif (ld.indexOf(lfilt) == -1 && lr.indexOf(lfilt) == -1 && ls.indexOf(lfilt) == -1)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// if we get here - we are displaying this task as a row\n\t\t\t\t// so fill in an array of objects for the row\n\t\t\t\tObject[] ro = new Object[13];\n\t\t\t\tro[0] = Integer.valueOf(task.getKey());\n\t\t\t\tro[1] = task.getState();\n\t\t\t\tro[2] = task.getType();\n\t\t\t\tro[12] = task.getCategory();\n\t\t\t\tro[3] = task.getPriority();\n\t\t\t\tro[6] = task.getStartDate();\n\t\t\t\tro[7] = task.getDueDate();\n\t\t\t\tro[8] = task.getCompletionDate();\n\n\t\t\t\t// duration\n\t\t\t\tif (task.getDueDate() != null) {\n\t\t\t\t\tro[9] = Integer.valueOf(DateUtil.daysBetween(task\n\t\t\t\t\t\t\t.getStartDate(), task.getDueDate()));\n\t\t\t\t} else {\n\t\t\t\t\tro[9] = null;\n\t\t\t\t}\n\n\t\t\t\tDate end = null;\n\t\t\t\tif (task.getState().equals(\n\t\t\t\t\t\ttasktypes.getFinalState(task.getType()))) {\n\t\t\t\t\tend = task.getCompletionDate();\n\t\t\t\t} else {\n\t\t\t\t\tend = new Date();\n\t\t\t\t}\n\n\t\t\t\t// elapsed time\n\t\t\t\tif (end == null) {\n\t\t\t\t\tro[10] = null;\n\t\t\t\t} else {\n\t\t\t\t\tro[10] = Integer.valueOf(DateUtil.daysBetween(task\n\t\t\t\t\t\t\t.getStartDate(), end));\n\t\t\t\t}\n\n\t\t\t\t// calculate days left - today - duedate\n\t\t\t\tif (ro[7] == null || TaskModel.isClosed(task))\n\t\t\t\t\tro[4] = null;\n\t\t\t\telse {\n\t\t\t\t\tDate dd = (Date) ro[7];\n\t\t\t\t\tro[4] = Integer.valueOf(DateUtil.daysLeft(dd));\n\t\t\t\t}\n\n\t\t\t\tro[5] = task.getSummary();\n\n\t\t\t\t// project\n\t\t\t\tStringBuffer ps = new StringBuffer();\n\t\t\t\tif (pid != null) {\n\t\t\t\t\tProject p = TaskModel.getReference().getProject(\n\t\t\t\t\t\t\tpid.intValue());\n\t\t\t\t\tif (p != null) {\n\t\t\t\t\t\tString tt = p.getDescription();\n\n\t\t\t\t\t\tfor (int i = 0; tt != null && i < tt.length(); i++) {\n\t\t\t\t\t\t\tchar c = tt.charAt(i);\n\t\t\t\t\t\t\tif (c == '\\n' || c == '\\r') {\n\t\t\t\t\t\t\t\tps.append(' ');\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tps.append(c);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tro[11] = ps;\n\n\t\t\t\t// add the task row to table\n\t\t\t\ttm.addRow(ro);\n\n\t\t\t\ttotalItems ++;\n\t\t\t\t\n\t\t\t\t// add subtasks if requested\n\t\t\t\tif( showSubTasksBox.isSelected())\n\t\t\t\t{\n\t\t\t\t\tCollection<Subtask> subtasks = TaskModel.getReference().getSubTasks(task.getKey());\n\t\t\t\t\tfor( Subtask subtask : subtasks )\n\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\tif ((taskStatus.equals(Resource.getResourceString(\"All_Open\")) || taskStatus.equals(Resource.getResourceString(\"OPEN\"))) && subtask.getCloseDate() != null) \n\t\t\t\t\t\t\t\tcontinue;\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\tif( showClosedTasksCheckBox != null && !showClosedTasksCheckBox.isSelected() && subtask.getCloseDate() != null)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\n\t\t\t\t\t\tro = new Object[12];\n\t\t\t\t\t\tro[0] = null;\n\t\t\t\t\t\tro[1] = (subtask.getCloseDate() == null) ? Resource.getResourceString(\"OPEN\"): Resource.getResourceString(\"CLOSED\");\n\t\t\t\t\t\tro[2] = subtaskString;\n\t\t\t\t\t\tro[11] = task.getCategory();\n\t\t\t\t\t\tro[3] = task.getPriority();\n\t\t\t\t\t\tro[6] = subtask.getStartDate();\n\t\t\t\t\t\tro[7] = subtask.getDueDate();\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (subtask.getDueDate() != null) {\n\t\t\t\t\t\t\tro[8] = Integer.valueOf(DateUtil.daysBetween(subtask\n\t\t\t\t\t\t\t\t\t.getStartDate(), subtask.getDueDate()));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tro[8] = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tend = null;\n\t\t\t\t\t\tif (subtask.getCloseDate() != null){\n\t\t\t\t\t\t\tend = subtask.getCloseDate();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tend = new Date();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// elapsed time\n\t\t\t\t\t\tif (end == null) {\n\t\t\t\t\t\t\tro[9] = null;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tro[9] = Integer.valueOf(DateUtil.daysBetween(subtask\n\t\t\t\t\t\t\t\t\t.getStartDate(), end));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// calculate days left - today - duedate\n\t\t\t\t\t\tif (ro[7] == null || subtask.getCloseDate() != null)\n\t\t\t\t\t\t\tro[4] = null;\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tDate dd = (Date) ro[7];\n\t\t\t\t\t\t\tro[4] = Integer.valueOf(DateUtil.daysLeft(dd));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tro[5] = subtask.getDescription();\n\t\t\t\t\t\t\n\t\t\t\t\t\tro[10] = ps;\n\t\t\t\t\t\t\n\t\t\t\t\t\ttm.addRow(ro);\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttm.tableChanged(new TableModelEvent(tm));\n\t\t\t}\n\t\t\tgetTotalLabel().setText(totalItems + \" items\");\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\t// apply default sort to the table if not sorted by user\n\t\tif (!tm.isSorted())\n\t\t\ttm.sortByColumn(6); // days left\n\t\telse\n\t\t\ttm.sort();\n\t}\n\n\t/**\n\t * Sets the filter criteria.\n\t * \n\t * @param projectName\n\t *            the project name\n\t * @param status\n\t *            the status\n\t * @param filter\n\t *            the filter string\n\t * @param caseSensitive\n\t *            the case sensitive match flag\n\t */\n\tpublic void setFilterCriteria(String projectName, String status,\n\t\t\tString filter, boolean caseSensitive) {\n\t\tthis.projectName = projectName;\n\t\tthis.taskStatus = status;\n\t\tthis.filterString = filter;\n\t\tthis.filterCaseSensitive = caseSensitive;\n\n\t}\n\n\t/**\n\t * open the task editor in add mode.\n\t */\n\tprivate void task_add() {\n\t\ttry {\n\t\t\t// fill in project if we have one\n\t\t\tInteger projfiltid = null;\n\t\t\tif (!projectName.equals(Resource.getResourceString(\"All\"))) {\n\t\t\t\ttry {\n\t\t\t\t\tprojfiltid = TaskView.getProjectId(projectName);\n\t\t\t\t\t\n\t\t\t\t\t// check if project exists\n\t\t\t\t\tProject p = TaskModel.getReference().getProject(projfiltid);\n\t\t\t\t\tif( p == null )\n\t\t\t\t\t{\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"project_not_found\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnew TaskView(null, TaskView.Action.ADD, projfiltid).showView();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t/**\n\t * open the task editor to edit a task.\n\t * \n\t * @param tasknum\n\t *            the tasknum\n\t */\n\tstatic private void task_change(int tasknum) {\n\n\t\ttry {\n\t\t\t// get the task from the data model\n\t\t\tTask task = TaskModel.getReference().getTask(tasknum);\n\t\t\tif (task == null)\n\t\t\t\treturn;\n\n\t\t\t// display the task editor\n\t\t\tnew TaskView(task, TaskView.Action.CHANGE, null).showView();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * close a task and open the editor on the clone\n\t * \n\t * @param tasknum\n\t *            the tasknum\n\t */\n\tstatic private void task_clone(int tasknum) {\n\n\t\ttry {\n\t\t\t// get the task\n\t\t\tTask task = TaskModel.getReference().getTask(tasknum);\n\t\t\tif (task == null)\n\t\t\t\treturn;\n\n\t\t\t// display the task editor\n\t\t\tnew TaskView(task, TaskView.Action.CLONE, null).showView();\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/TaskModule.java",
    "content": "package net.sf.borg.ui.task;\n\nimport java.awt.Component;\nimport java.awt.GridBagConstraints;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.JComponent;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport javax.swing.JTabbedPane;\n\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.Module;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.TrayIconProxy;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\n\n/**\n * The TaskModule is the UI Module for Tasks that is invoked from the MultiView\n * \n */\npublic class TaskModule extends DockableView implements Module {\n\n\t\n\tprivate static final long serialVersionUID = 1L;\n\t/**\n\t * the task tabs managed by thsi module - which are all subtabs on the MultiView's task tab\n\t */\n\tprivate JTabbedPane taskTabs;\n\t\n\tprivate boolean isInitialized = false;\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn Resource.getResourceString(\"tasks\");\n\t}\n\n\t@Override\n\tpublic JComponent getComponent() {\n\t\t\n\t\tif( !isInitialized )\n\t\t{\n\t\t\ttaskTabs = new JTabbedPane();\n\n\t\t\ttaskTabs.addTab(Resource.getResourceString(\"project_tree\"),\n\t\t\t\t\tnew ProjectTreePanel());\n\n\t\t\ttaskTabs.addTab(Resource.getResourceString(\"projects\"),\n\t\t\t\t\tnew ProjectPanel());\n\n\t\t\ttaskTabs.addTab(Resource.getResourceString(\"tasks\"),\n\t\t\t\t\tnew TaskFilterPanel());\n\t\t\t\n\t\t\tsetLayout(new java.awt.GridBagLayout());\n\t\t\tadd(taskTabs, GridBagConstraintsFactory\n\t\t\t\t\t.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\t\t\tisInitialized = true;\n\n\n\t\t}\n\t\t\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void initialize(MultiView parent) {\n\t\t\n\t\tfinal MultiView par = parent;\n\t\tparent.addToolBarItem(IconHelper.getIcon(\n\t\t\"/resource/Task16.gif\"), getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.TASK);\n\t\t\t}\n\t\t});\n\t\tTrayIconProxy.addAction(getModuleName(), new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tpar.setView(ViewType.TASK);\n\t\t\t}\n\t\t});\n\t\t\n\t\tJMenuItem edittypes = new JMenuItem();\n\t\tJMenuItem resetst = new JMenuItem();\n\t\tResourceHelper.setText(edittypes, \"edit_types\");\n\t\tedittypes.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\ttry {\n\t\t\t\t\tTaskConfigurator.getReference().setVisible(true);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tMultiView.getMainView().addOptionsMenuItem(edittypes);\n\n\t\tResourceHelper.setText(resetst, \"Reset_Task_States_to_Default\");\n\t\tresetst.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent evt) {\n\t\t\t\tresetstActionPerformed();\n\t\t\t}\n\t\t});\n\n\t\tMultiView.getMainView().addOptionsMenuItem(resetst);\n\t\t\n\t}\n\t\n\t/**\n\t * reset task state action\n\t */\n\tprivate static void resetstActionPerformed() {\n\t\ttry {\n\t\t\tString msg = Resource.getResourceString(\"reset_state_warning\");\n\t\t\tint ret = JOptionPane.showConfirmDialog(null, msg, Resource\n\t\t\t\t\t.getResourceString(\"Import_WARNING\"),\n\t\t\t\t\tJOptionPane.OK_CANCEL_OPTION);\n\n\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\treturn;\n\t\t\tTaskModel taskmod_ = TaskModel.getReference();\n\t\t\ttaskmod_.getTaskTypes().loadDefault();\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void print() {\n\t\tComponent c = taskTabs.getSelectedComponent();\n\t\tif( c instanceof ProjectPanel )\n\t\t\t((ProjectPanel)c).print();\n\t\telse if( c instanceof TaskListPanel )\n\t\t\t((TaskListPanel)c).print();\n\t\telse if( c instanceof TaskFilterPanel )\n\t\t\t((TaskFilterPanel)c).print();\n\t\telse\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"No_Print\"));\n\n\t\t\n\t}\n\t\n\t@Override\n\tpublic ViewType getViewType() {\n\t\treturn ViewType.TASK;\n\t}\n\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn this.getModuleName();\n\t}\n\n\n\t@Override\n\tpublic void refresh() {\n\t\t// do nothing - children do their own refresh\n\t\t\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\t// do nothing - children do their own refresh\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/TaskView.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.task;\n\nimport java.awt.Component;\nimport java.awt.Container;\nimport java.awt.Dimension;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Iterator;\nimport java.util.Vector;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JScrollPane;\nimport javax.swing.JSplitPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.JTable;\nimport javax.swing.JTextArea;\nimport javax.swing.JTextField;\nimport javax.swing.JViewport;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.event.TableModelListener;\nimport javax.swing.table.DefaultTableCellRenderer;\n\nimport com.toedter.calendar.JDateChooser;\nimport com.toedter.calendar.JDateChooserCellEditor;\n\nimport net.sf.borg.common.DateUtil;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\nimport net.sf.borg.model.CategoryModel;\nimport net.sf.borg.model.LinkModel;\nimport net.sf.borg.model.Model.ChangeEvent;\nimport net.sf.borg.model.Model.ChangeEvent.ChangeAction;\nimport net.sf.borg.model.TaskModel;\nimport net.sf.borg.model.entity.Project;\nimport net.sf.borg.model.entity.Subtask;\nimport net.sf.borg.model.entity.Task;\nimport net.sf.borg.model.entity.Tasklog;\nimport net.sf.borg.ui.DockableView;\nimport net.sf.borg.ui.MultiView;\nimport net.sf.borg.ui.MultiView.ViewType;\nimport net.sf.borg.ui.ResourceHelper;\nimport net.sf.borg.ui.link.LinkPanel;\nimport net.sf.borg.ui.util.DateDialog;\nimport net.sf.borg.ui.util.GridBagConstraintsFactory;\nimport net.sf.borg.ui.util.IconHelper;\nimport net.sf.borg.ui.util.LimitDocument;\nimport net.sf.borg.ui.util.PlainDateEditor;\nimport net.sf.borg.ui.util.PopupMenuHelper;\nimport net.sf.borg.ui.util.TableSorter;\n\n/**\n * UI for Viewing and Editing individual Tasks and their Subtasks\n */\npublic class TaskView extends DockableView {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Actions being taken on a task when starting the editor\n\t */\n\tpublic enum Action {\n\n\t\tADD, CHANGE, CLONE\n\t}\n\n\t/**\n\t * Gets the project id form a project string containing id and name.\n\t * \n\t * @param s the project string from the project combo box\n\t * \n\t * @return the project id\n\t * \n\t * @throws Exception\n\t */\n\tstatic public Integer getProjectId(String s) throws Exception {\n\t\tint i = s.indexOf(\":\");\n\t\tif (i == -1)\n\t\t\tthrow new Exception(\"Cannot parse project label\");\n\t\tString ss = s.substring(0, i);\n\n\t\tint pid = Integer.parseInt(ss);\n\t\treturn Integer.valueOf(pid);\n\n\t}\n\n\t/**\n\t * Gets the project string for a project to put in the project combo box\n\t * \n\t * @param p the project\n\t * \n\t * @return the project string\n\t */\n\tstatic public String getProjectString(Project p) {\n\t\tString desc = p.getDescription();\n\t\tif (desc != null && desc.length() > 20) {\n\t\t\tdesc = desc.substring(0, 20) + \"...\";\n\t\t}\n\t\treturn p.getKey() + \":\" + desc;\n\t}\n\n\t/** The link panel. */\n\tprivate LinkPanel linkPanel;\n\n\t/** The category combo box. */\n\tprivate JComboBox<String> categoryComboBox = null;\n\n\t/** The close date. */\n\tprivate JTextField closeDate = null;\n\n\t/** The days left text. */\n\tprivate JTextField daysLeftText = null;\n\n\t/** The due date chooser. */\n\tprivate JDateChooser dueDateChooser;\n\n\t/** The parent project. */\n\tprivate Integer parentProject = null;\n\n\t/** The task id text. */\n\tprivate JTextField taskIdText;\n\n\t/** The task tabbed panel. */\n\tprivate JTabbedPane taskTabbedPanel;\n\n\tprivate JTextField summaryText;\n\n\t/** The description text. */\n\tprivate JTextArea descriptionText;\n\n\t/** The resolution text. */\n\tprivate JTextArea resolutionText;\n\n\t/** The log table. */\n\tprivate final JTable logtable = new JTable();\n\n\t/** The person assigned text. */\n\tprivate JTextField personAssignedText;\n\n\t/** The priority text. */\n\tprivate JComboBox<Integer> priorityText;\n\n\t/** The project combo box. */\n\tprivate final JComboBox<String> projectComboBox = new JComboBox<String>();\n\n\t/** The sub task table. */\n\tprivate final JTable subTaskTable = new JTable();\n\n\t/** The start date chooser. */\n\tprivate JDateChooser startDateChooser;\n\n\t/** The status combo box. */\n\tprivate JComboBox<String> statusComboBox;\n\n\t/** The sub task ids to be deleted. */\n\tprivate final ArrayList<Integer> subTaskIdsToBeDeleted = new ArrayList<Integer>();\n\n\t/** The window title. */\n\tprivate String windowTitle = \"\";\n\n\t/** The task type combo box. */\n\tprivate JComboBox<String> taskTypeComboBox;\n\t\n\n\t/**\n\t * constructor\n\t * \n\t * @param task      the task\n\t * @param function  the action being taken on the task\n\t * @param projectid the projectid or null if no initial project\n\t * \n\t * @throws Exception\n\t */\n\tpublic TaskView(Task task, Action function, Integer projectid) throws Exception {\n\t\tsuper();\n\n\t\t// listen for model changes\n\t\taddModel(LinkModel.getReference()); // to update link tab color\n\t\taddModel(TaskModel.getReference());\n\n\t\tparentProject = projectid;\n\n\t\tinitComponents(); // init the GUI widgets\n\n\t\tinitSubtaskTable();\n\t\tinitLogTable();\n\n\t\t// set size of text area\n\t\tdescriptionText.setRows(15);\n\t\tdescriptionText.setColumns(40);\n\n\t\t// load categories\n\t\ttry {\n\t\t\tCollection<String> cats = CategoryModel.getReference().getCategories();\n\t\t\tIterator<String> it = cats.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tcategoryComboBox.addItem(it.next());\n\t\t\t}\n\t\t\tcategoryComboBox.setSelectedIndex(0);\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t}\n\n\t\t// show the task\n\t\tshowtask(function, task);\n\n\t\trefresh();\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see net.sf.borg.ui.DockableView#getFrameTitle()\n\t */\n\t@Override\n\tpublic String getFrameTitle() {\n\t\treturn windowTitle;\n\t}\n\n\t/**\n\t * Gets the selected subtask ids.\n\t * \n\t * @return the selected subtask ids\n\t */\n\tprivate Integer[] getSelectedSubtaskIds() {\n\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\tint[] indices = subTaskTable.getSelectedRows();\n\t\tInteger[] ret = new Integer[indices.length];\n\n\t\tfor (int i = 0; i < indices.length; ++i) {\n\t\t\tint index = indices[i];\n\t\t\tret[i] = (Integer) ts.getValueAt(index, 0);\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Initialize the UI\n\t */\n\tprivate void initComponents() {\n\t\t/*\n\t\t * this was one of the worst code-generated messes in borg. It is mostly cleaned\n\t\t * up now, but is not perfect.\n\t\t */\n\n\t\tsetLayout(new GridBagLayout());\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\n\t\t/*\n\t\t * information panel\n\t\t */\n\n\t\tJPanel taskInformationPanel = new JPanel();\n\t\ttaskInformationPanel.setLayout(new GridBagLayout());\n\n\t\ttaskInformationPanel.setBorder(new TitledBorder(Resource.getResourceString(\"TaskInformation\")));\n\n\t\ttaskIdText = new JTextField();\n\t\tJLabel lblItemNum = new JLabel();\n\t\tJLabel lblStatus = new JLabel();\n\t\tstartDateChooser = new JDateChooser(new PlainDateEditor());\n\t\tdueDateChooser = new JDateChooser(new PlainDateEditor());\n\t\tpriorityText = new JComboBox<Integer>();\n\t\tfor (int p = 1; p <= 5; p++) {\n\t\t\tpriorityText.addItem(Integer.valueOf(p));\n\t\t}\n\n\t\tpersonAssignedText = new JTextField(new LimitDocument(10), null, 10);\n\t\tJLabel lblStartDate = new JLabel();\n\t\tJLabel lblDueDate = new JLabel();\n\t\tJLabel lblPri = new JLabel();\n\t\tJLabel lblPA = new JLabel();\n\t\tJLabel lblType = new JLabel();\n\t\tstatusComboBox = new JComboBox<String>();\n\t\ttaskTypeComboBox = new JComboBox<String>();\n\t\tJLabel categoryLabel = new JLabel();\n\n\t\tJLabel closeLabel = new JLabel();\n\t\tcloseLabel.setText(\"\");\n\n\t\tJLabel daysLeftLabel = new JLabel();\n\t\tdaysLeftLabel.setText(Resource.getResourceString(\"Days_Left\"));\n\t\tdaysLeftText = new JTextField();\n\t\tdaysLeftText.setEditable(false);\n\n\t\tcloseDate = new JTextField();\n\t\tcloseDate.setEditable(false);\n\t\tResourceHelper.setText(closeLabel, \"close_date\");\n\n\t\tJLabel prLabel = new JLabel(Resource.getResourceString(\"project\"));\n\n\t\ttaskIdText.setText(\"taskIdText\");\n\n\t\tResourceHelper.setText(lblItemNum, \"Item_#\");\n\t\tlblItemNum.setLabelFor(taskIdText);\n\n\t\tResourceHelper.setText(lblStatus, \"Status\");\n\t\tlblStatus.setLabelFor(statusComboBox);\n\n\t\tResourceHelper.setText(lblStartDate, \"Start_Date\");\n\t\tlblStartDate.setLabelFor(startDateChooser);\n\n\t\tResourceHelper.setText(lblDueDate, \"Due_Date\");\n\t\tlblDueDate.setLabelFor(dueDateChooser);\n\n\t\tResourceHelper.setText(lblPri, \"Pri\");\n\t\tlblPri.setLabelFor(priorityText);\n\n\t\tResourceHelper.setText(lblPA, \"PA\");\n\t\tlblPA.setLabelFor(personAssignedText);\n\n\t\tResourceHelper.setText(lblType, \"Type\");\n\t\tlblType.setLabelFor(taskTypeComboBox);\n\n\t\tResourceHelper.setText(categoryLabel, \"Category\");\n\t\tcategoryComboBox = new JComboBox<String>();\n\t\tcategoryLabel.setLabelFor(categoryComboBox);\n\n\t\tsummaryText = new JTextField();\n\n\t\ttaskInformationPanel.add(lblItemNum, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(lblStatus, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(lblType, GridBagConstraintsFactory.create(0, 2, GridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\ttaskInformationPanel.add(taskIdText, GridBagConstraintsFactory.create(1, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(statusComboBox,\n\t\t\t\tGridBagConstraintsFactory.create(1, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(taskTypeComboBox,\n\t\t\t\tGridBagConstraintsFactory.create(1, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\ttaskInformationPanel.add(categoryLabel,\n\t\t\t\tGridBagConstraintsFactory.create(2, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(prLabel, GridBagConstraintsFactory.create(2, 1, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(lblPri, GridBagConstraintsFactory.create(2, 2, GridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\ttaskInformationPanel.add(categoryComboBox,\n\t\t\t\tGridBagConstraintsFactory.create(3, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(projectComboBox,\n\t\t\t\tGridBagConstraintsFactory.create(3, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(priorityText,\n\t\t\t\tGridBagConstraintsFactory.create(3, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\ttaskInformationPanel.add(lblStartDate,\n\t\t\t\tGridBagConstraintsFactory.create(4, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(lblDueDate, GridBagConstraintsFactory.create(4, 1, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(closeLabel, GridBagConstraintsFactory.create(4, 2, GridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\ttaskInformationPanel.add(startDateChooser,\n\t\t\t\tGridBagConstraintsFactory.create(5, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(dueDateChooser,\n\t\t\t\tGridBagConstraintsFactory.create(5, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(closeDate, GridBagConstraintsFactory.create(5, 2, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\ttaskInformationPanel.add(lblPA, GridBagConstraintsFactory.create(6, 0, GridBagConstraints.BOTH, 0.0, 0.0));\n\t\ttaskInformationPanel.add(daysLeftLabel,\n\t\t\t\tGridBagConstraintsFactory.create(6, 1, GridBagConstraints.BOTH, 0.0, 0.0));\n\n\t\ttaskInformationPanel.add(personAssignedText,\n\t\t\t\tGridBagConstraintsFactory.create(7, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\t\ttaskInformationPanel.add(daysLeftText,\n\t\t\t\tGridBagConstraintsFactory.create(7, 1, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\ttopPanel.add(taskInformationPanel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tJLabel l = new JLabel();\n\t\tl.setText(Resource.getResourceString(\"summary\"));\n\t\ttaskInformationPanel.add(l, GridBagConstraintsFactory.create(0, 3, GridBagConstraints.BOTH));\n\n\t\tGridBagConstraints gbc = GridBagConstraintsFactory.create(1, 3, GridBagConstraints.BOTH);\n\t\tgbc.gridwidth = GridBagConstraints.REMAINDER;\n\t\ttaskInformationPanel.add(summaryText, gbc);\n\n\t\t/*\n\t\t * task tabbed panel\n\t\t */\n\t\ttaskTabbedPanel = new JTabbedPane();\n\n\t\tJScrollPane descriptionScroll = new JScrollPane();\n\t\tdescriptionText = new JTextArea(new LimitDocument(Prefs.getIntPref(PrefName.MAX_TEXT_SIZE)));\n\t\tdescriptionText.setLineWrap(true);\n\t\tdescriptionText.setName(\"Description\");\n\t\tdescriptionScroll.setViewportView(descriptionText);\n\t\ttaskTabbedPanel.addTab(Resource.getResourceString(\"Description\"), descriptionScroll);\n\n\t\tJScrollPane resolutionScroll = new JScrollPane();\n\t\tresolutionText = new JTextArea(new LimitDocument(Prefs.getIntPref(PrefName.MAX_TEXT_SIZE)));\n\t\tresolutionText.setLineWrap(true);\n\t\tresolutionScroll.setViewportView(resolutionText);\n\t\ttaskTabbedPanel.addTab(Resource.getResourceString(\"Resolution\"), resolutionScroll);\n\n\t\tJScrollPane logPane = new JScrollPane();\n\t\tlogPane.setViewportView(logtable);\n\t\ttaskTabbedPanel.addTab(Resource.getResourceString(\"history\"), logPane);\n\n\t\tlinkPanel = new LinkPanel();\n\t\ttaskTabbedPanel.addTab(Resource.getResourceString(\"links\"), linkPanel);\n\n\t\t/*\n\t\t * sub task panel\n\t\t */\n\t\tJScrollPane subTaskScroll = new JScrollPane();\n\t\tsubTaskScroll.setPreferredSize(new Dimension(300, 300));\n\t\tsubTaskScroll.setViewportView(subTaskTable);\n\n\t\tJPanel subTaskPanel = new JPanel();\n\t\tsubTaskPanel.setLayout(new GridBagLayout());\n\t\tsubTaskPanel.setBorder(new TitledBorder(Resource.getResourceString(\"SubTasks\")));\n\t\tsubTaskPanel.add(subTaskScroll, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * split pane\n\t\t */\n\t\tJSplitPane taskSplitPane = new JSplitPane();\n\t\ttaskSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);\n\t\ttaskSplitPane.setBottomComponent(subTaskPanel);\n\t\ttaskSplitPane.setPreferredSize(new Dimension(400, 400));\n\t\ttaskSplitPane.setDividerLocation(200);\n\t\ttaskSplitPane.setOneTouchExpandable(true);\n\t\ttaskSplitPane.setTopComponent(taskTabbedPanel);\n\n\t\ttopPanel.add(taskSplitPane, GridBagConstraintsFactory.create(0, 1, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * button panel\n\t\t */\n\t\tJPanel buttonPanel = new JPanel();\n\t\tJButton savebutton = new JButton();\n\t\ttopPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 2));\n\n\t\tsavebutton.setIcon(IconHelper.getIcon(\"/resource/Save16.gif\"));\n\t\tResourceHelper.setText(savebutton, \"Save\");\n\t\tsavebutton.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\t\t\t\tsavetask();\n\t\t\t}\n\t\t});\n\n\t\tbuttonPanel.add(savebutton, savebutton.getName());\n\n\t\tadd(topPanel, GridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH, 1.0, 1.0));\n\n\t}\n\n\t/**\n\t * Initializes the log table.\n\t */\n\tprivate void initLogTable() {\n\n\t\tlogtable.setModel(new TableSorter(\n\t\t\t\tnew String[] { Resource.getResourceString(\"Date\"), Resource.getResourceString(\"Description\"), },\n\t\t\t\tnew Class[] { Date.class, String.class }, new boolean[] { false, false }));\n\n\t\tlogtable.getColumnModel().getColumn(0).setPreferredWidth(5);\n\t\tlogtable.getColumnModel().getColumn(1).setPreferredWidth(300);\n\n\t\tlogtable.setShowGrid(true);\n\t\tlogtable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\tTableSorter ts = (TableSorter) logtable.getModel();\n\n\t\t// sort by date\n\t\tts.sortByColumn(0);\n\t\tts.addMouseListenerToHeaderInTable(logtable);\n\n\t\t// popup menu\n\t\tnew PopupMenuHelper(logtable, new PopupMenuHelper.Entry[] { new PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\n\t\t\t\tString tasknum = taskIdText.getText();\n\t\t\t\tif (tasknum.equals(\"CLONE\") || tasknum.equals(\"NEW\"))\n\t\t\t\t\treturn;\n\t\t\t\tString logentry = JOptionPane\n\t\t\t\t\t\t.showInputDialog(net.sf.borg.common.Resource.getResourceString(\"Enter_Log\"));\n\t\t\t\tif (logentry == null)\n\t\t\t\t\treturn;\n\n\t\t\t\ttry {\n\t\t\t\t\tTaskModel.getReference().addLog(Integer.parseInt(tasknum), logentry);\n\t\t\t\t\tloadLog(Integer.parseInt(tasknum));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}, \"Add_Log\"), });\n\n\t}\n\t\n\tstatic private class DaysLeftCellRenderer extends DefaultTableCellRenderer {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\t\tprivate final ImageIcon redIcon = IconHelper.getIcon(\n\t\t\t\t\"/resource/red.png\");\n\t\tprivate final ImageIcon orangeIcon = IconHelper.getIcon(\"/resource/orange.png\");\n\t\tprivate final ImageIcon yellowIcon = IconHelper.getIcon(\"/resource/yellow.png\");\n\t\tprivate final ImageIcon greenIcon = IconHelper.getIcon(\"/resource/green.png\");\n\t\tprivate final ImageIcon emptyIcon = IconHelper.getIcon(\"/resource/empty.png\");\n\t\t//private final ImageIcon doneIcon = new ImageIcon(getClass()\n\t\t//\t\t.getResource(\"/resource/done.png\"));\n\n\n\t\t@Override\n\t\tpublic Component getTableCellRendererComponent(JTable table, Object value,\n\t    \t\tboolean isSelected, boolean hasFocus, int row, int column) {\n\n\t    \tJLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\n\n\t\t\tlabel.setIcon(null);\n\t\t\tsetHorizontalAlignment(JLabel.CENTER);\n\t\t\t\n\t\t\tif( value != null ) {\n\t\t\t\t\n\t\t\t\tInteger daysLeft = (Integer) value;\n\t\t\t\t\n\t\t\t\tTableSorter ts = (TableSorter) table.getModel();\n\n\t\t\t\tBoolean closed = (Boolean)ts.getValueAt(row, 1);\n\t\t\t\t\n\t\t\t\tif( closed != null && closed == true)\n\t\t\t\t\tthis.setIcon(null);\n\t\t\t\telse if (daysLeft < Prefs\n\t\t\t\t\t\t.getIntPref(PrefName.RED_DAYS))\n\t\t\t\t\tthis.setIcon(redIcon);\n\t\t\t\telse if (daysLeft < Prefs\n\t\t\t\t\t\t.getIntPref(PrefName.ORANGE_DAYS))\n\t\t\t\t\tthis.setIcon(orangeIcon);\n\t\t\t\telse if (daysLeft < Prefs\n\t\t\t\t\t\t.getIntPref(PrefName.YELLOW_DAYS))\n\t\t\t\t\tthis.setIcon(yellowIcon);\n\t\t\t\telse if (daysLeft == TaskModel.NO_DAYS_VALUE)\n\t\t\t\t\tthis.setIcon(emptyIcon);\n\t\t\t\telse\n\t\t\t\t\tthis.setIcon(greenIcon);\n\t\t\t}\n\n\t    \treturn label;\n\t    }\n\t}\n\n\t/**\n\t * Initialize the subtask table.\n\t */\n\tprivate void initSubtaskTable() {\n\n\t\tsubTaskTable.setModel(new TableSorter(\n\t\t\t\tnew String[] { Resource.getResourceString(\"subtask_id\"), Resource.getResourceString(\"Closed\"), \n\t\t\t\t\t\tResource.getResourceString(\"Description\"), Resource.getResourceString(\"Start_Date\"),\n\t\t\t\t\t\tResource.getResourceString(\"Due_Date\"), Resource.getResourceString(\"duration\"),\n\t\t\t\t\t\tResource.getResourceString(\"Days_Left\"), Resource.getResourceString(\"close_date\") },\n\t\t\t\tnew Class[] { Integer.class, java.lang.Boolean.class, java.lang.String.class, Date.class, Date.class,\n\t\t\t\t\t\tInteger.class, Integer.class, Date.class },\n\t\t\t\tnew boolean[] { false, true, true, true, true, false, false, false }));\n\n\t\tsubTaskTable.getColumnModel().getColumn(0).setPreferredWidth(5);\n\t\tsubTaskTable.getColumnModel().getColumn(1).setPreferredWidth(5);\n\t\tsubTaskTable.getColumnModel().getColumn(2).setPreferredWidth(300);\n\t\tsubTaskTable.getColumnModel().getColumn(3).setPreferredWidth(30);\n\t\tsubTaskTable.getColumnModel().getColumn(4).setPreferredWidth(30);\n\t\tsubTaskTable.getColumnModel().getColumn(5).setPreferredWidth(30);\n\t\tsubTaskTable.getColumnModel().getColumn(6).setPreferredWidth(30);\n\t\tsubTaskTable.getColumnModel().getColumn(7).setPreferredWidth(30);\n\t\t\n\t\tDefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();\n\t\tcenterRenderer.setHorizontalAlignment(JLabel.CENTER);\n\t\tsubTaskTable.setDefaultRenderer(Integer.class, centerRenderer);\n\t\t\n\t\tsubTaskTable.getColumnModel().getColumn(6).setCellRenderer(new DaysLeftCellRenderer());\n\n\t\t// use a date chooser to edit subtask dates in the table\n\t\tsubTaskTable.setDefaultEditor(Date.class, new JDateChooserCellEditor());\n\n\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\tsubTaskTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n\n\t\tsubTaskTable.getModel().addTableModelListener(new TableModelListener() {\n\n\t\t\t@Override\n\t\t\tpublic void tableChanged(TableModelEvent arg0) {\n\n\t\t\t\t// ignore the table sorters events - we only care about the\n\t\t\t\t// underlying table model - which is tablesorter.newtablemodel\n\t\t\t\t// tablesorter is crap\n\t\t\t\tif (arg0.getSource() instanceof TableSorter)\n\t\t\t\t\treturn;\n\n\t\t\t\t// ignore insert - this only happens when blank rows are added\n\t\t\t\tif (arg0.getType() == TableModelEvent.INSERT)\n\t\t\t\t\treturn;\n\n\t\t\t\t// always maintain a new \"add\" row as the table is updated\n\t\t\t\t// (without\n\t\t\t\t// being saved)\n\n\t\t\t\t// ignore delete events when deciding to add a blank row\n\t\t\t\t// problem caused when the table is cleared\n\t\t\t\tif (arg0.getType() == TableModelEvent.DELETE)\n\t\t\t\t\treturn;\n\n\t\t\t\tTableSorter model = (TableSorter) subTaskTable.getModel();\n\t\t\t\tfor (int i = 0; i < model.getRowCount(); i++) {\n\t\t\t\t\tString text = (String) model.getValueAt(i, 2);\n\t\t\t\t\tif (text == null || text.isEmpty()) {\n\t\t\t\t\t\t// already has a blank row - so don't add one\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tObject[] o = { null, Boolean.valueOf(false), null, null, null, null, null };\n\t\t\t\tmodel.addRow(o);\n\t\t\t}\n\n\t\t});\n\n\t\tsubTaskTable.setShowGrid(true);\n\t\tsubTaskTable.setIntercellSpacing(new Dimension(1, 1));\n\n\t\t// sort by due date\n\t\t// ts.sortByColumn(4);\n\t\tts.addMouseListenerToHeaderInTable(subTaskTable);\n\n\t\tfinal JPopupMenu stmenu = PopupMenuHelper.createPopupMenu(new PopupMenuHelper.Entry[] {\n\n\t\t\t\tnew PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\n\t\t\t\t\t\tTableSorter ts2 = (TableSorter) subTaskTable.getModel();\n\t\t\t\t\t\tInteger[] ids = getSelectedSubtaskIds();\n\t\t\t\t\t\tfor (int i = 0; i < ids.length; ++i) {\n\t\t\t\t\t\t\tif (ids[i] == null)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tfor (int row = 0; row < ts2.getRowCount(); row++) {\n\t\t\t\t\t\t\t\tInteger rowid = (Integer) ts2.getValueAt(row, 0);\n\t\t\t\t\t\t\t\tif (rowid != null && rowid.intValue() == ids[i].intValue()) {\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 4);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, \"Clear_DueDate\"), new PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\n\t\t\t\t\t\tTableSorter ts2 = (TableSorter) subTaskTable.getModel();\n\n\t\t\t\t\t\tint[] indices = subTaskTable.getSelectedRows();\n\t\t\t\t\t\tif (indices.length == 0)\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// prompt user for due date\n\t\t\t\t\t\tDateDialog dlg = new DateDialog(null);\n\t\t\t\t\t\tdlg.setCalendar(new GregorianCalendar());\n\t\t\t\t\t\tdlg.setVisible(true);\n\t\t\t\t\t\tCalendar dlgcal = dlg.getCalendar();\n\t\t\t\t\t\tif (dlgcal == null)\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// set the due date\n\t\t\t\t\t\tInteger[] ids = getSelectedSubtaskIds();\n\t\t\t\t\t\tfor (int i = 0; i < ids.length; ++i) {\n\t\t\t\t\t\t\tif (ids[i] == null)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tfor (int row = 0; row < ts2.getRowCount(); row++) {\n\t\t\t\t\t\t\t\tInteger rowid = (Integer) ts2.getValueAt(row, 0);\n\t\t\t\t\t\t\t\tif (rowid != null && rowid.intValue() == ids[i].intValue()) {\n\t\t\t\t\t\t\t\t\tts2.setValueAt(dlgcal.getTime(), row, 4);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, \"Set_DueDate\"), new PopupMenuHelper.Entry(new ActionListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void actionPerformed(ActionEvent evt) {\n\n\t\t\t\t\t\tTableSorter ts2 = (TableSorter) subTaskTable.getModel();\n\t\t\t\t\t\tInteger[] ids = getSelectedSubtaskIds();\n\t\t\t\t\t\tif (ids.length == 0)\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// confirm delete\n\t\t\t\t\t\tint ret = JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\t\t\tResource.getResourceString(\"Really_Delete_\") + \"?\",\n\t\t\t\t\t\t\t\tResource.getResourceString(\"Confirm_Delete\"), JOptionPane.OK_CANCEL_OPTION,\n\t\t\t\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\t\t\t\tif (ret != JOptionPane.OK_OPTION)\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// to delete, we have to save the id in a list for\n\t\t\t\t\t\t// deletion and\n\t\t\t\t\t\t// null out the table rows so it is not added back\n\t\t\t\t\t\tfor (int i = 0; i < ids.length; ++i) {\n\t\t\t\t\t\t\tif (ids[i] == null)\n\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\tsubTaskIdsToBeDeleted.add(ids[i]);\n\n\t\t\t\t\t\t\tfor (int row = 0; row < ts2.getRowCount(); row++) {\n\t\t\t\t\t\t\t\tInteger rowid = (Integer) ts2.getValueAt(row, 0);\n\t\t\t\t\t\t\t\tif (rowid != null && rowid.intValue() == ids[i].intValue()) {\n\t\t\t\t\t\t\t\t\t// clear the row\n\t\t\t\t\t\t\t\t\tts2.setValueAt(Boolean.valueOf(false), row, 1);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 0);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 2);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 3);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 4);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 5);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 6);\n\t\t\t\t\t\t\t\t\tts2.setValueAt(null, row, 7);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// if table is now empty - add 1 row back so the user\n\t\t\t\t\t\t// can edit\n\t\t\t\t\t\tif (ts2.getRowCount() == 0) {\n\t\t\t\t\t\t\tinsertSubtask();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, \"Delete\"), });\n\n\t\tsubTaskTable.addMouseListener(new MouseAdapter() {\n\t\t\tprivate void maybeShowPopup(MouseEvent e) {\n\t\t\t\tif (e.isPopupTrigger()) {\n\t\t\t\t\tint row = subTaskTable.rowAtPoint(e.getPoint());\n\t\t\t\t\tif (row != -1 && !subTaskTable.isRowSelected(row)) {\n\t\t\t\t\t\tsubTaskTable.getSelectionModel().setSelectionInterval(row, row);\n\t\t\t\t\t}\n\t\t\t\t\tif (rowsSelected())\n\t\t\t\t\t\tstmenu.show(e.getComponent(), e.getX(), e.getY());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\t\tmaybeShowPopup(e);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\t\tmaybeShowPopup(e);\n\t\t\t}\n\t\t});\n\n\t}\n\n\tprivate boolean rowsSelected() {\n\t\tInteger[] ids = getSelectedSubtaskIds();\n\t\tfor (int i = 0; i < ids.length; ++i) {\n\t\t\tif (ids[i] != null)\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Insert a blank subtask row in the table\n\t */\n\tprivate void insertSubtask() {\n\t\tObject[] o = { null, Boolean.valueOf(false), null, null, null, null, null };\n\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\tts.addRow(o);\n\t}\n\n\t/**\n\t * Load the tasklog table for a given task\n\t * \n\t * @param taskid the task id\n\t * \n\t * @throws Exception\n\t */\n\tprivate void loadLog(int taskid) throws Exception {\n\n\t\tTableSorter tslog = (TableSorter) logtable.getModel();\n\t\t// clear rows\n\t\ttslog.setRowCount(0);\n\n\t\t// add log entries\n\t\tCollection<Tasklog> logs = TaskModel.getReference().getLogs(taskid);\n\t\tfor (Tasklog log : logs) {\n\t\t\tObject[] o = { log.getLogTime(), log.getDescription() };\n\t\t\ttslog.addRow(o);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void refresh() {\n\n\t}\n\n\t@Override\n\tpublic void update(ChangeEvent event) {\n\t\trefresh();\n\n\t\t// check if the item being edited was deleted\n\t\tif (event.getAction() == ChangeAction.DELETE && event.getObject() instanceof Task\n\t\t\t\t&& ((Task) event.getObject()).getKey() == getShownId())\n\t\t\ttry {\n\t\t\t\tshowtask(Action.ADD, null);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t}\n\n\t/**\n\t * Save subtasks for a task from the UI\n\t * \n\t * @param task the task\n\t * \n\t * @throws Warning\n\t * @throws Exception\n\t */\n\tprivate void saveSubtasks(Task task) throws Warning, Exception {\n\n\t\tint tasknum = task.getKey();\n\n\t\t// delete subtasks marked for deletion\n\t\tfor (Integer id : subTaskIdsToBeDeleted) {\n\t\t\tTaskModel.getReference().deleteSubTask(id.intValue());\n\t\t\tTaskModel.getReference().addLog(tasknum,\n\t\t\t\t\tResource.getResourceString(\"subtask\") + \" \" + id + \" \" + Resource.getResourceString(\"deleted\"));\n\t\t}\n\n\t\tsubTaskIdsToBeDeleted.clear();\n\n\t\t// stop editing\n\t\tif (subTaskTable.isEditing())\n\t\t\tsubTaskTable.getCellEditor().stopCellEditing();\n\n\t\t// loop through subtask rows\n\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\tfor (int r = 0; r < subTaskTable.getRowCount(); r++) {\n\n\t\t\t// if no description - there is no subtask in this row\n\t\t\tObject desc = ts.getValueAt(r, 2);\n\t\t\tif (desc == null || desc.equals(\"\"))\n\t\t\t\tcontinue;\n\n\t\t\t// get subtask fields\n\t\t\tInteger id = (Integer) ts.getValueAt(r, 0);\n\n\t\t\tBoolean closed = (Boolean) ts.getValueAt(r, 1);\n\n\t\t\t// do not allow adding new open subtasks if task is closed\n\t\t\tif (TaskModel.isClosed(task) && id == null && closed.booleanValue() == false) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tDate crd = (Date) ts.getValueAt(r, 3);\n\t\t\t// default subtask start date to the task start date unless the task\n\t\t\t// started in the past\n\t\t\tif (crd == null && task.getStartDate().before(new Date()))\n\t\t\t\tcrd = new Date();\n\t\t\telse if (crd == null)\n\t\t\t\tcrd = task.getStartDate();\n\t\t\tDate dd = (Date) ts.getValueAt(r, 4);\n\t\t\tDate cd = (Date) ts.getValueAt(r, 7);\n\n\t\t\t// check if the subtask is being closed\n\t\t\tboolean closing = false;\n\t\t\tif (closed.booleanValue() == true && cd == null) {\n\t\t\t\tcd = new Date();\n\t\t\t\tclosing = true;\n\t\t\t} else if (closed.booleanValue() == false && cd != null)\n\t\t\t\tcd = null;\n\n\t\t\tSubtask s = new Subtask();\n\t\t\tif (id != null)\n\t\t\t\ts.setKey(id.intValue());\n\t\t\ts.setDescription((String) desc);\n\t\t\ts.setCloseDate(cd);\n\t\t\ts.setDueDate(dd);\n\n\t\t\t// validate dd - make sure only date and not time is compared\n\t\t\tif (closed.booleanValue() != true && dd != null && task.getDueDate() != null) {\n\t\t\t\tif (DateUtil.isAfter(dd, task.getDueDate())) {\n\t\t\t\t\tString msg = Resource.getResourceString(\"stdd_warning\") + \": \" + desc;\n\t\t\t\t\tthrow new Warning(msg);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\ts.setStartDate(crd);\n\n\t\t\t// validate that subtask does not start before task\n\t\t\tif (closed.booleanValue() != true && crd != null && task.getStartDate() != null) {\n\t\t\t\tif (DateUtil.isAfter(task.getStartDate(), crd)) {\n\t\t\t\t\tString msg = Resource.getResourceString(\"stsd_warning\") + \": \" + desc;\n\t\t\t\t\tthrow new Warning(msg);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (s.getStartDate() != null && s.getDueDate() != null\n\t\t\t\t\t&& DateUtil.isAfter(s.getStartDate(), s.getDueDate())) {\n\t\t\t\tString msg = Resource.getResourceString(\"sd_dd_warn\") + \": \" + desc;\n\t\t\t\tthrow new Warning(msg);\n\t\t\t}\n\n\t\t\ts.setTask(Integer.valueOf(tasknum));\n\n\t\t\tTaskModel.getReference().saveSubTask(s);\n\n\t\t\tif (id == null || id.intValue() == 0) {\n\t\t\t\tTaskModel.getReference().addLog(tasknum, Resource.getResourceString(\"subtask\") + \" \" + s.getKey() + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"created\") + \": \" + s.getDescription());\n\t\t\t}\n\t\t\tif (closing) {\n\t\t\t\tTaskModel.getReference().addLog(tasknum, Resource.getResourceString(\"subtask\") + \" \" + s.getKey() + \" \"\n\t\t\t\t\t\t+ Resource.getResourceString(\"Closed\") + \": \" + s.getDescription());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Save the current task\n\t */\n\tprivate void savetask() {\n\n\t\t// validate description\n\t\tif (summaryText.getText() == null || summaryText.getText().trim().equals(\"\")) {\n\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"empty_summ\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\n\t\t\tString num = taskIdText.getText();\n\n\t\t\tTaskModel.getReference();\n\t\t\t// need to use a transaction as we update a number of tables and may\n\t\t\t// need\n\t\t\t// to roll them all back together\n\t\t\tTaskModel.beginTransaction();\n\n\t\t\tTask task = TaskModel.getReference().newMR();\n\n\t\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\t\tif (num.equals(\"NEW\")) {\n\n\t\t\t\t// set to initial state\n\t\t\t\ttask.setState(TaskModel.getReference().getTaskTypes()\n\t\t\t\t\t\t.getInitialState((String) taskTypeComboBox.getSelectedItem()));\n\t\t\t} else if (num.equals(\"CLONE\")) {\n\t\t\t\t// set to initial state\n\t\t\t\ttask.setState(TaskModel.getReference().getTaskTypes()\n\t\t\t\t\t\t.getInitialState((String) taskTypeComboBox.getSelectedItem()));\n\t\t\t} else {\n\t\t\t\ttask.setKey(Integer.valueOf(num).intValue());\n\t\t\t\ttask.setState((String) statusComboBox.getSelectedItem());\n\t\t\t}\n\n\t\t\t// fill in the task fields from the screen\n\t\t\ttask.setType((String) taskTypeComboBox.getSelectedItem()); // type\n\t\t\tCalendar cal = startDateChooser.getCalendar();\n\t\t\tif (cal == null)\n\t\t\t\tcal = new GregorianCalendar();\n\t\t\ttask.setStartDate(cal.getTime()); // start date\n\t\t\tcal = dueDateChooser.getCalendar();\n\t\t\tif (cal != null)\n\t\t\t\ttask.setDueDate(cal.getTime()); // due date\n\n\t\t\t// validate due date\n\t\t\tif (task.getDueDate() != null && DateUtil.isAfter(task.getStartDate(), task.getDueDate())) {\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"sd_dd_warn\"));\n\t\t\t}\n\n\t\t\tInteger pri = (Integer) priorityText.getSelectedItem();\n\t\t\ttask.setPriority(pri);\n\t\t\ttask.setPersonAssigned(personAssignedText.getText());\n\t\t\ttask.setSummary(summaryText.getText());\n\t\t\ttask.setDescription(descriptionText.getText());\n\t\t\ttask.setResolution(resolutionText.getText());\n\n\t\t\tString cat = (String) categoryComboBox.getSelectedItem();\n\t\t\tif (cat.equals(\"\") || cat.equals(CategoryModel.UNCATEGORIZED)) {\n\t\t\t\ttask.setCategory(null);\n\t\t\t} else {\n\t\t\t\ttask.setCategory(cat);\n\t\t\t}\n\n\t\t\ttask.setProject(null);\n\t\t\tString proj = (String) projectComboBox.getSelectedItem();\n\t\t\ttry {\n\t\t\t\ttask.setProject(getProjectId(proj));\n\t\t\t} catch (Exception e) {\n\t\t\t\t// no project selected\n\t\t\t}\n\n\t\t\tInteger pid = task.getProject();\n\t\t\tif (pid != null && task.getStartDate() != null) {\n\t\t\t\tProject p = TaskModel.getReference().getProject(pid);\n\t\t\t\tif (p.getStartDate() != null && DateUtil.isAfter(p.getStartDate(), task.getStartDate())) {\n\t\t\t\t\tthrow new Warning(Resource.getResourceString(\"proj_sd_warning\"));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (cat.equals(\"\") || cat.equals(CategoryModel.UNCATEGORIZED)) {\n\t\t\t\ttask.setCategory(null);\n\t\t\t} else {\n\t\t\t\ttask.setCategory(cat);\n\t\t\t}\n\n\t\t\t// do not close task if subtasks are open\n\t\t\tif (TaskModel.isClosed(task)) {\n\t\t\t\tfor (int r = 0; r < subTaskTable.getRowCount(); r++) {\n\t\t\t\t\tBoolean closed = (Boolean) ts.getValueAt(r, 1);\n\t\t\t\t\tInteger id = (Integer) ts.getValueAt(r, 0);\n\t\t\t\t\tif (id == null || id.intValue() == 0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (closed.booleanValue() != true) {\n\t\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"open_subtasks\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// fix invalid task if type was changed\n\t\t\tCollection<String> states = TaskModel.getReference().getTaskTypes().getStates(task.getType());\n\t\t\tif (!states.contains(task.getState())) {\n\t\t\t\ttask.setState(TaskModel.getReference().getTaskTypes().getInitialState(task.getType()));\n\t\t\t}\n\n\t\t\t// save the task to the DB\n\t\t\tTask orig = TaskModel.getReference().getTask(task.getKey());\n\n\t\t\tif (orig != null) {\n\t\t\t\ttask.setUid(orig.getUid());\n\t\t\t\ttask.setUrl(orig.getUrl());\n\t\t\t\ttask.setCreateTime(orig.getCreateTime());\n\t\t\t\ttask.setLastMod(orig.getLastMod());\n\t\t\t}\n\n\t\t\tTaskModel.getReference().savetask(task);\n\n\t\t\t// add various task log records\n\t\t\tif (num.equals(\"NEW\") || num.equals(\"CLONE\")) {\n\t\t\t\tTaskModel.getReference().addLog(task.getKey(), Resource.getResourceString(\"Task_Created\"));\n\t\t\t} else {\n\n\t\t\t\tif (orig != null && !orig.getState().equals(task.getState())) {\n\t\t\t\t\tTaskModel.getReference().addLog(task.getKey(), Resource.getResourceString(\"State_Change\") + \": \"\n\t\t\t\t\t\t\t+ orig.getState() + \" --> \" + task.getState());\n\t\t\t\t}\n\n\t\t\t\tString newd = \"null\";\n\t\t\t\tif (task.getDueDate() != null) {\n\t\t\t\t\tnewd = DateFormat.getDateInstance().format(task.getDueDate());\n\t\t\t\t}\n\n\t\t\t\tString oldd = \"null\";\n\t\t\t\tif (orig != null && orig.getDueDate() != null)\n\t\t\t\t\toldd = DateFormat.getDateInstance().format(orig.getDueDate());\n\t\t\t\tif (orig != null && !newd.equals(oldd)) {\n\t\t\t\t\tTaskModel.getReference().addLog(task.getKey(), Resource.getResourceString(\"DueDate\") + \" \"\n\t\t\t\t\t\t\t+ Resource.getResourceString(\"Change\") + \": \" + oldd + \" --> \" + newd);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// save subtasks\n\t\t\tsaveSubtasks(task);\n\n\t\t\tTaskModel.getReference();\n\t\t\t// can commit now - task, subtasks, tasklogs\n\t\t\tTaskModel.commitTransaction();\n\n\t\t\t// remove this view\n\t\t\tContainer p = this.getParent();\n\t\t\tif (p instanceof JViewport) {\n\t\t\t\t// special case - if we are docked inside a project tree window,\n\t\t\t\t// then do not remove\n\t\t\t\t// ourself\n\t\t\t\tshowtask(Action.CHANGE, task);\n\t\t\t} else\n\t\t\t\tthis.close();\n\n\t\t\t// go back to task view when saving a task\n\t\t\tMultiView.getMainView().setView(ViewType.TASK);\n\n\t\t} catch (Warning w) {\n\t\t\tErrmsg.getErrorHandler().notice(w.getMessage());\n\t\t\ttry {\n\t\t\t\tTaskModel.getReference();\n\t\t\t\tTaskModel.rollbackTransaction();\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\ttry {\n\t\t\t\tTaskModel.getReference();\n\t\t\t\tTaskModel.rollbackTransaction();\n\t\t\t} catch (Exception e1) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e1);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected PrefName getFrameSizePref() {\n\t\treturn new PrefName(Resource.getResourceString(\"Item\") + \"_framesize\", \"-1,-1,800,600,N\");\n\t}\n\n\t/**\n\t * load the UI from a task\n\t * \n\t * @param function the action being taken on the task\n\t * @param task     the task\n\t * \n\t * @throws Exception\n\t * \n\t */\n\tprivate void showtask(Action function, Task task) throws Exception {\n\n\t\tTableSorter ts = (TableSorter) subTaskTable.getModel();\n\t\tts.setRowCount(0);\n\n\t\tsubTaskIdsToBeDeleted.clear();\n\n\t\tprojectComboBox.removeAllItems();\n\t\tprojectComboBox.addItem(\"\");\n\n\t\t// populate parent project combo box\n\t\tCollection<Project> projects = TaskModel.getReference().getProjects();\n\t\tif (projects != null) {\n\t\t\tfor (Project p : projects) {\n\t\t\t\tif (p.getStatus().equals(Resource.getResourceString(\"OPEN\")))\n\t\t\t\t\tprojectComboBox.addItem(getProjectString(p));\n\t\t\t}\n\t\t}\n\n\t\t// if we are showing an existing task - fill in the gui fields from it\n\t\tif (task != null) {\n\n\t\t\t// task number\n\t\t\ttaskIdText.setText(Integer.toString(task.getKey()));\n\t\t\ttaskIdText.setEditable(false);\n\n\t\t\t// window title - \"Item N\"\n\t\t\twindowTitle = Resource.getResourceString(\"Item_\") + \" \" + task.getKey();\n\n\t\t\t// due date\n\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\tDate dd = task.getDueDate();\n\t\t\tif (dd != null) {\n\t\t\t\tgc.setTime(dd);\n\t\t\t\tdueDateChooser.setCalendar(gc);\n\t\t\t}\n\n\t\t\tGregorianCalendar gc2 = new GregorianCalendar();\n\t\t\tdd = task.getStartDate();\n\t\t\tif (dd != null)\n\t\t\t\tgc2.setTime(dd);\n\t\t\tstartDateChooser.setCalendar(gc2);\n\n\t\t\tpriorityText.setSelectedItem(task.getPriority());\n\t\t\tpersonAssignedText.setText(task.getPersonAssigned());\n\n\t\t\tDate cd = task.getCompletionDate();\n\t\t\tif (cd != null)\n\t\t\t\tcloseDate.setText(DateFormat.getDateInstance(DateFormat.MEDIUM).format(cd));\n\n\t\t\tint daysleft = DateUtil.daysLeft(task.getDueDate());\n\t\t\tdaysLeftText.setText(Integer.toString(daysleft));\n\n\t\t\tString cat = task.getCategory();\n\t\t\tif (cat != null && !cat.equals(\"\")) {\n\t\t\t\tcategoryComboBox.setSelectedItem(cat);\n\t\t\t} else {\n\t\t\t\tcategoryComboBox.setSelectedIndex(0);\n\t\t\t}\n\n\t\t\tdescriptionText.setText(task.getDescription());\n\t\t\tsummaryText.setText(task.getSummary());\n\t\t\tresolutionText.setText(task.getResolution());\n\n\t\t\tstatusComboBox.addItem(task.getState());\n\t\t\tstatusComboBox.setEditable(false);\n\n\t\t\t// type\n\t\t\tString type = task.getType();\n\t\t\ttaskTypeComboBox.removeAllItems();\n\t\t\ttaskTypeComboBox.addItem(type);\n\n\t\t\t// if not in initial state - disable change of type\n\t\t\tString initState = TaskModel.getReference().getTaskTypes().getInitialState(type);\n\t\t\tif (initState.equals(task.getState())) {\n\t\t\t\ttaskTypeComboBox.setEnabled(true);\n\t\t\t\tVector<String> tv = TaskModel.getReference().getTaskTypes().getTaskTypes();\n\t\t\t\tfor (int i = 0; i < tv.size(); i++) {\n\t\t\t\t\ttaskTypeComboBox.addItem(tv.elementAt(i));\n\t\t\t\t}\n\t\t\t} else\n\t\t\t\ttaskTypeComboBox.setEnabled(false);\n\n\t\t\t// add subtasks\n\t\t\tCollection<Subtask> subtasks = TaskModel.getReference().getSubTasks(task.getKey());\n\t\t\tfor (Subtask subtask : subtasks) {\n\t\t\t\tObject[] o = { Integer.valueOf(subtask.getKey()), subtask.getCloseDate() == null ? Boolean.valueOf(false) : Boolean.valueOf(true),\n\t\t\t\t\t\t subtask.getDescription(), subtask.getStartDate(),\n\t\t\t\t\t\tsubtask.getDueDate(),\n\t\t\t\t\t\tsubtask.getDueDate() != null\n\t\t\t\t\t\t\t\t? Integer.valueOf(DateUtil.daysBetween(subtask.getStartDate(), subtask.getDueDate()))\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tsubtask.getDueDate() != null ? Integer.valueOf(DateUtil.daysLeft(subtask.getDueDate())) : null,\n\t\t\t\t\t\tsubtask.getCloseDate() };\n\n\t\t\t\tts.addRow(o);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// load tasklogs\n\t\t\t\tloadLog(task.getKey());\n\t\t\t} catch (Warning w) {\n\t\t\t\t// empty\n\t\t\t}\n\n\t\t\tInteger pid = task.getProject();\n\t\t\tif (pid != null) {\n\t\t\t\tProject p = TaskModel.getReference().getProject(pid.intValue());\n\t\t\t\tif (TaskModel.isClosed(p)) {\n\t\t\t\t\tprojectComboBox.addItem(getProjectString(p));\n\t\t\t\t}\n\t\t\t\tprojectComboBox.setSelectedItem(getProjectString(p));\n\n\t\t\t}\n\n\t\t\tlinkPanel.setOwner(task);\n\n\t\t} else // initialize new task\n\t\t{\n\n\t\t\tlinkPanel.setOwner(null);\n\n\t\t\t// task number = NEW\n\t\t\ttaskIdText.setText(\"NEW\");\n\t\t\ttaskIdText.setEditable(false);\n\n\t\t\t// title\n\t\t\twindowTitle = Resource.getResourceString(\"NEW_Item\");\n\n\t\t\tpriorityText.setSelectedItem(Integer.valueOf(3)); // priority default to\n\t\t\t// 3\n\t\t\tpersonAssignedText.setText(\"\");\n\t\t\tcategoryComboBox.setSelectedIndex(0);\n\t\t\tdescriptionText.setText(\"\");\n\t\t\tresolutionText.setText(\"\");\n\n\t\t\t// add task types to select from - only for new task\n\t\t\tVector<String> tv = TaskModel.getReference().getTaskTypes().getTaskTypes();\n\t\t\tfor (int i = 0; i < tv.size(); i++) {\n\t\t\t\ttaskTypeComboBox.addItem(tv.elementAt(i));\n\t\t\t}\n\n\t\t\t// if a parent project already set - then initialize some fields\n\t\t\t// from it\n\t\t\tif (parentProject != null) {\n\t\t\t\tProject p = TaskModel.getReference().getProject(parentProject.intValue());\n\t\t\t\tif (p == null) {\n\t\t\t\t\tErrmsg.getErrorHandler().notice(Resource.getResourceString(\"project_not_found\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tprojectComboBox.setSelectedItem(getProjectString(p));\n\n\t\t\t\tString cat = p.getCategory();\n\t\t\t\tif (cat != null && !cat.equals(\"\")) {\n\t\t\t\t\tcategoryComboBox.setSelectedItem(cat);\n\t\t\t\t} else {\n\t\t\t\t\tcategoryComboBox.setSelectedIndex(0);\n\t\t\t\t}\n\n\t\t\t\tGregorianCalendar gc = new GregorianCalendar();\n\t\t\t\tDate dd = p.getDueDate();\n\t\t\t\tif (dd != null) {\n\t\t\t\t\tgc.setTime(dd);\n\t\t\t\t\tdueDateChooser.setCalendar(gc);\n\t\t\t\t}\n\n\t\t\t\tDate sd = p.getStartDate();\n\t\t\t\t// only use the project start date if it is in the future\n\t\t\t\tif (sd != null && sd.after(new Date())) {\n\t\t\t\t\tgc.setTime(sd);\n\t\t\t\t\tstartDateChooser.setCalendar(gc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// cannot change status on a new task - will always go to initial state\n\t\t// depending on type\n\t\tif (task == null) {\n\t\t\tstatusComboBox.setEnabled(false);\n\t\t}\n\n\t\t// cloning takes the fields filled in for an existing task and resets\n\t\t// only those\n\t\t// that don't apply to the clone\n\t\tif (function == Action.CLONE) {\n\t\t\t// need new task number\n\t\t\ttaskIdText.setText(\"CLONE\");\n\t\t\ttaskIdText.setEditable(false);\n\n\t\t\tstatusComboBox.removeAllItems();\n\t\t\tstatusComboBox.addItem(TaskModel.getReference().getTaskTypes()\n\t\t\t\t\t.getInitialState(taskTypeComboBox.getSelectedItem().toString()));\n\t\t\tstatusComboBox.setEnabled(false);\n\n\t\t\tstartDateChooser.setCalendar(null);\n\t\t\tdueDateChooser.setCalendar(null);\n\t\t\tcloseDate.setText(null);\n\t\t\t\n\t\t\t// reset all subtask id's - but keep the subtasks - which will be\n\t\t\t// saved with new ids\n\t\t\tfor (int row = 0; row < subTaskTable.getRowCount(); row++) {\n\t\t\t\tsubTaskTable.setValueAt(null, row, 0);\n\t\t\t\tsubTaskTable.setValueAt(false, row, 1);\n\t\t\t\tsubTaskTable.setValueAt(null, row, 3);\n\t\t\t\tsubTaskTable.setValueAt(null, row, 4);\n\t\t\t\tsubTaskTable.setValueAt(null, row, 5);\n\t\t\t\tsubTaskTable.setValueAt(null, row, 6);\n\t\t\t\tsubTaskTable.setValueAt(null, row, 7);\n\t\t\t}\n\n\t\t}\n\t\t// edit existing task\n\t\telse if (function == Action.CHANGE && task != null) {\n\n\t\t\t// determine valid next states based on task type and current\n\t\t\t// state\n\t\t\tString state = task.getState();\n\t\t\tString type = task.getType();\n\t\t\tCollection<String> v = TaskModel.getReference().getTaskTypes().nextStates(type, state);\n\n\t\t\t// set next state pulldown\n\t\t\tstatusComboBox.removeAllItems();\n\t\t\tfor (String s : v) {\n\t\t\t\tstatusComboBox.addItem(s);\n\t\t\t}\n\t\t\tstatusComboBox.setSelectedItem(state);\n\t\t\tstatusComboBox.setEnabled(true);\n\n\t\t}\n\n\t\t// always add an empty row for quick editing\n\t\tinsertSubtask();\n\t}\n\n\tpublic int getShownId() {\n\t\tString num = taskIdText.getText();\n\t\tif (!num.equals(\"NEW\") && !num.equals(\"CLONE\"))\n\t\t\treturn Integer.parseInt(num);\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tprotected void cleanUp() {\n\t\tsuper.cleanUp();\n\t\tif (linkPanel != null)\n\t\t\tlinkPanel.cleanup();\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/task/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  UI classes that handle Tasks and related classes (Tasks, Projects, Subtasks, Tasklog)\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/ColorChooserButton.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\n\n/**\n * A button for choosing colors. Shows an icon with the color.\n * This replaces the old color chooser button that could paint it's background different colors.\n * That worked inconsistently for various look and feels that also painted the backgrounds.\n */\npublic class ColorChooserButton extends JButton {\n\t\n\tstatic private class ColorIcon implements Icon {\n\n\t\tprivate Color color = Color.BLACK;\n\t\tstatic private final int height = 10;\n\t\tstatic private final int width = 30;\n\n\t\t/**\n\t\t * Instantiates a new toggle button icon.\n\t\t * \n\t\t * @param col\n\t\t *            the color\n\t\t */\n\t\tpublic ColorIcon(Color col) {\n\t\t\tcolor = col;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconHeight()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconHeight() {\n\t\t\treturn height;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconWidth()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconWidth() {\n\t\t\treturn width;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#paintIcon(java.awt.Component,\n\t\t * java.awt.Graphics, int, int)\n\t\t */\n\t\t@Override\n\t\tpublic void paintIcon(Component c, Graphics g, int x, int y) {\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\tg2.setColor(Color.BLACK);\n\t\t\tg2.drawRect(x, y, width, height);\n\t\t\tg2.setColor(color);\n\t\t\tg2.fillRect(x, y, width, height);\n\t\t}\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\tprotected Color colorProperty;\n\t\n\t\n\tpublic ColorChooserButton( String p_text, Color p_color ){\n\t\tsetText( p_text );\n\t\tsetColorProperty( p_color );\n\t\taddActionListener(new ModalListener());\n\t      \n\t}\n\t\n\t/**\n\t * @return Returns the color.\n\t */\n\tpublic Color getColorProperty() {\n\t\treturn colorProperty;\n\t}\n\t/**\n\t * @param color The color to set.\n\t */\n\tpublic void setColorProperty(Color color) {\n\t\tthis.colorProperty = color;\n\t\tsetIcon( new ColorIcon(color));\n\t}\n\t\n\tprivate class ModalListener implements ActionListener{\n\t\t@Override\n\t\tpublic void actionPerformed(ActionEvent event){\n\t\t\tColor selected = JColorChooser.showDialog(\n\t\t\t\tnull, \n\t\t\t\t\"\", \n\t\t\t\tgetColorProperty());\n\t\t\tif( selected != null )\n\t\t\t\tsetColorProperty(selected);\n      }\n   }\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/ColorComboBox.java",
    "content": "package net.sf.borg.ui.util;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.model.Theme;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ItemEvent;\nimport java.awt.event.ItemListener;\n\npublic class ColorComboBox extends JComboBox<String>{\n\n  \n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n     * renders the color selection pull-down with colored boxes as the choices.\n     */\n    static private class ColorBoxRenderer extends JLabel implements ListCellRenderer<Object> {\n\n        private static final long serialVersionUID = 1L;\n\n        @Getter\n        @Setter\n        private Color chosenColor = Color.black;\n\n        /**\n         * constructor.\n         */\n        public ColorBoxRenderer() {\n            setOpaque(true);\n            setHorizontalAlignment(CENTER);\n            setVerticalAlignment(CENTER);\n        }\n\n        /**\n         * get the color choice label for a given color value.\n         */\n        @Override\n        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,\n                                                      boolean cellHasFocus) {\n            String sel = (String) value;\n\n            if (isSelected) {\n                setBackground(list.getSelectionBackground());\n            } else {\n                setBackground(list.getBackground());\n            }\n\n            // no text - the label will be icon only\n            setText(\" \");\n\n            // set the icon based on the user defined colors\n            // map the \"logical\" color names to the user defined color\n            Theme t = Theme.getCurrentTheme();\n            if (sel.equals(colors[3])) {\n                setIcon(new SolidComboBoxIcon(new Color(t.getTextColor4())));\n            } else if (sel.equals(colors[0])) {\n                setIcon(new SolidComboBoxIcon(new Color(t.getTextColor1())));\n            } else if (sel.equals(colors[1])) {\n                setIcon(new SolidComboBoxIcon(new Color(t.getTextColor2())));\n            } else if (sel.equals(colors[2])) {\n                setIcon(new SolidComboBoxIcon(new Color(t.getTextColor3())));\n            } else if (sel.equals(colors[4])) {\n                setIcon(new SolidComboBoxIcon(new Color(t.getTextColor5())));\n            } else if (sel.equals(\"chosen\")) {\n                setIcon(new SolidComboBoxIcon(chosenColor));\n            } else if (sel.equals(\"choose\")) {\n                setText(Resource.getResourceString(\"choose\"));\n                setIcon(null);\n            } else {\n                // just for strike-through, we use text\n                setText(Resource.getResourceString(\"strike\"));\n                setIcon(null);\n            }\n\n            return this;\n        }\n\n    }\n\n    static private class ComboItemListener implements ItemListener {\n\n        @Getter\n        @Setter\n        private boolean active = true;\n\n        private final ColorBoxRenderer cbr;\n        private final JComboBox<String> box;\n\n        public ComboItemListener(ColorBoxRenderer cbr, JComboBox<String> colorComboBox) {\n            this.cbr = cbr;\n            box = colorComboBox;\n        }\n\n        @Override\n        public void itemStateChanged(ItemEvent e) {\n            if (!active)\n                return;\n            if (e.getStateChange() == ItemEvent.SELECTED) {\n                String l = (String) e.getItem();\n                if (l.equals(\"choose\")) {\n                    Color selected = JColorChooser.showDialog(null, \"\", cbr.getChosenColor());\n                    if (selected != null)\n                        cbr.setChosenColor(selected);\n                    box.setSelectedIndex(6);\n                }\n\n            }\n        }\n\n    }\n\n    /**\n     * Long, thin, rectangular icon that goes in the color chooser pulldown list\n     * items.\n     */\n    static private class SolidComboBoxIcon implements Icon {\n\n        private Color color = Color.BLACK; // color\n        static private final int h = 10; // height\n        static private final int w = 60; // width\n\n        /**\n         * Instantiates a new solid combo box icon.\n         *\n         * @param col\n         *            the color\n         */\n        public SolidComboBoxIcon(Color col) {\n            color = col;\n        }\n\n        /*\n         * (non-Javadoc)\n         *\n         * @see Icon#getIconHeight()\n         */\n        @Override\n        public int getIconHeight() {\n            return h;\n        }\n\n        /*\n         * (non-Javadoc)\n         *\n         * @see Icon#getIconWidth()\n         */\n        @Override\n        public int getIconWidth() {\n            return w;\n        }\n\n        /*\n         * (non-Javadoc)\n         *\n         * @see Icon#paintIcon(java.awt.Component, java.awt.Graphics, int, int)\n         */\n        @Override\n        public void paintIcon(Component c, Graphics g, int x, int y) {\n            Graphics2D g2 = (Graphics2D) g;\n            g2.setColor(Color.BLACK);\n            g2.drawRect(x, y, w, h);\n            g2.setColor(color);\n            g2.fillRect(x, y, w, h);\n        }\n    }\n\n    private final ColorBoxRenderer cbr = new ColorBoxRenderer();\n\n    private final ComboItemListener comboItemListener = new ComboItemListener(cbr, this);\n\n    private static final String[] colors = { Theme.COLOR1, Theme.COLOR2, Theme.COLOR3, Theme.COLOR4, Theme.COLOR5, \"strike\", \"chosen\", \"choose\" };\n\n    public ColorComboBox() {\n\n        this.setOpaque(false);\n        this.setRenderer(cbr);\n        this.setEditable(false);\n        for (int i = 0; i < colors.length; i++) {\n            this.addItem(colors[i]);\n        }\n\n        this.addItemListener(comboItemListener);\n    }\n\n    public Color getChosenColor(){\n        return cbr.getChosenColor();\n    }\n\n    public void setChosenColor(Color color){\n        cbr.setChosenColor(color);\n    }\n\n    public void setActive(boolean active){\n        comboItemListener.setActive(active);\n    }\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/DateDialog.java",
    "content": "/*\n * This file is part of BORG.\n *\n * BORG is free software; you can redistribute it and/or modify it under the\n * terms of the GNU General Public License as published by the Free Software\n * Foundation; either version 2 of the License, or (at your option) any later\n * version.\n *\n * BORG is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with\n * BORG; if not, write to the Free Software Foundation, Inc., 59 Temple Place,\n * Suite 330, Boston, MA 02111-1307 USA\n *\n * Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\nimport com.toedter.calendar.JDateChooser;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.ui.ResourceHelper;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\nimport java.util.Calendar;\n\n/**\n * A dialog for picking a date that wraps JDateChooser\n */\npublic class DateDialog extends JDialog {\n\t\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The date. */\n\tprivate Calendar calendar;\n\n\t/** The date combo box. */\n\tprivate JDateChooser dateComboBox;\n\n\t/**\n\t * constructor\n\t * \n\t * @param frmParent the parent frame\n\t */\n\tpublic DateDialog(Frame frmParent) {\n\t\tsuper(frmParent, Resource.getResourceString(\"Enter_Date\"), true);\n\t\tinitUI();\n\t}\n\n\t/**\n\t * Gets the calendar.\n\t * \n\t * @return the calendar\n\t */\n\tpublic final Calendar getCalendar() {\n\t\treturn calendar;\n\t}\n\n\t/**\n\t * Inits the ui.\n\t */\n\tprivate void initUI() {\n\t\tJPanel pnlMain = new JPanel();\n\t\tgetContentPane().add(pnlMain);\n\t\tpnlMain.setLayout(new BorderLayout());\n\n\t\tJPanel pnlInputAndIcon = new JPanel();\n\t\tpnlMain.add(pnlInputAndIcon, BorderLayout.CENTER);\n\t\tpnlInputAndIcon.setLayout(new BorderLayout());\n\n\t\tJPanel pnlInput = new JPanel();\n\t\tpnlInputAndIcon.add(pnlInput, BorderLayout.CENTER);\n\t\tpnlInput.setLayout(new BorderLayout());\n\n\t\tJPanel pnlIcon = new JPanel();\n\t\tpnlInputAndIcon.add(pnlIcon, BorderLayout.WEST);\n\t\tpnlIcon.setLayout(new BorderLayout());\n\n\t\tJPanel pnlFields = new JPanel();\n\t\tpnlInput.add(pnlFields, BorderLayout.CENTER);\n\t\tpnlFields.setLayout(new GridLayout(0, 1));\n\t\tpnlFields.add(dateComboBox = new JDateChooser(new PlainDateEditor()));\n\n\t\tJPanel pnlLabels = new JPanel();\n\t\tpnlInput.add(pnlLabels, BorderLayout.WEST);\n\t\tpnlLabels.setLayout(new GridLayout(0, 1));\n\n\t\tJLabel lblDate;\n\t\tpnlLabels.add(lblDate = new JLabel());\n\n\t\tResourceHelper.setText(lblDate, Resource.getResourceString(\"Date\"));\n\t\tlblDate.setLabelFor(dateComboBox);\n\t\tlblDate.setText(lblDate.getText() + \":\");\n\n\t\tJPanel pnlButtons = new JPanel();\n\t\tpnlButtons.setLayout(new FlowLayout(FlowLayout.CENTER));\n\t\tpnlMain.add(pnlButtons, BorderLayout.SOUTH);\n\t\tJButton bn;\n\t\tpnlButtons.add(bn = new JButton(Resource.getResourceString(\"OK\")));\n\t\tgetRootPane().setDefaultButton(bn);\n\t\t\n\t\tbn.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcalendar = dateComboBox.getCalendar();\n\t\t\t\tif( calendar == null ) return;\n\t\t\t\tsetVisible(false);\n\t\t\t}\n\t\t});\n\n\t\tpnlButtons.add(bn = new JButton(Resource.getResourceString(\"Cancel\")));\n\t\tActionListener cancelListener = new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcalendar = null;\n\t\t\t\tsetVisible(false);\n\t\t\t}\n\t\t};\n\t\tbn.addActionListener(cancelListener);\n\t\tgetRootPane().registerKeyboardAction(cancelListener,\n\t\t\t\tKeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),\n\t\t\t\tJComponent.WHEN_IN_FOCUSED_WINDOW);\t\tpack();\n\n\t\t// Make it a little wider\n\t\tDimension dim = getSize();\n\t\tdim.width += 40;\n\t\tsetSize(dim);\n\t\tsetLocationRelativeTo(null);\n\t}\n\n\t/**\n\t * Sets the calendar.\n\t * \n\t * @param cal the new calendar\n\t */\n\tpublic final void setCalendar(Calendar cal) {\n\t\tdateComboBox.setCalendar(cal);\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/DateTimePanel.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2012 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\nimport com.toedter.calendar.JDateChooser;\nimport net.sf.borg.common.Resource;\nimport net.sf.borg.common.Warning;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionListener;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\n\n/**\n * Creates a JPanel that lets the user select a date and time\n */\npublic class DateTimePanel extends JPanel {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic static void main(String[] args) {\n\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tJFrame f = new JFrame();\n\t\t\t\tf.setContentPane(new DateTimePanel(true, true));\n\t\t\t\tf.pack();\n\t\t\t\tf.setVisible(true);\n\t\t\t\tJFrame f2 = new JFrame();\n\t\t\t\tf2.setContentPane(new DateTimePanel(false, false));\n\t\t\t\tf2.pack();\n\t\t\t\tf2.setVisible(true);\n\t\t\t}\n\t\t});\n\n\t}\n\n\t/** The ampm box. */\n\tprivate final JComboBox<String> ampmBox = new JComboBox<String>();\n\n\tprivate final DefaultComboBoxModel<String> ampmModel = new DefaultComboBoxModel<String>(\n\t\t\tnew String[] { \"AM\", \"PM\" });\n\n\tprivate final JDateChooser dateChooser = new JDateChooser(new PlainDateEditor());\n\n\t/** The hour box. */\n\tprivate final JComboBox<String> hourBox = new JComboBox<String>();\n\t\n\t/**\n\t * combo box choices for setting the hour if we are using 24-hour time\n\t */\n\tprivate final DefaultComboBoxModel<String> milHourModel = new DefaultComboBoxModel<String>(\n\t\t\tnew String[] { \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\",\n\t\t\t\t\t\"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\",\n\t\t\t\t\t\"20\", \"21\", \"22\", \"23\" });\n\n\tprivate boolean milTime = false;\n\n\tprivate final DefaultComboBoxModel<String> minModel = new DefaultComboBoxModel<String>(\n\t\t\tnew String[] { \"00\", \"05\", \"10\", \"15\", \"20\", \"25\", \"30\", \"35\",\n\t\t\t\t\t\"40\", \"45\", \"50\", \"55\" });\n\n\t/** The minute box. */\n\tprivate final JComboBox<String> minuteBox = new JComboBox<String>();\n\n\t/**\n\t * combo box choices for setting 12-hr time\n\t */\n\tprivate final DefaultComboBoxModel<String> normHourModel = new DefaultComboBoxModel<String>(\n\t\t\tnew String[] { \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\",\n\t\t\t\t\t\"11\", \"12\" });\n\n\tprivate boolean showDate = true;\n\n\t\n\tpublic DateTimePanel(boolean showDate, boolean milTime) {\n\t\tsuper();\n\t\tthis.showDate = showDate;\n\t\tthis.milTime = milTime;\n\t\tinitialize();\n\t}\n\n\t/**\n\t * add an action listener that gets notified if the time changes\n\t * @param l\n\t */\n\tpublic void addTimeListener(ActionListener l) {\n\t\thourBox.addActionListener(l);\n\t\tminuteBox.addActionListener(l);\n\t\tampmBox.addActionListener(l);\n\t}\n\n\t/**\n\t * Gets the time shown in the panel.\n\t * \n\t * @return the time\n\t * @throws Warning\n\t */\n\tpublic Date getTime() throws Warning {\n\t\tCalendar cal = dateChooser.getCalendar();\n\t\tif (milTime) {\n\t\t\tcal.set(Calendar.HOUR_OF_DAY, hourBox.getSelectedIndex());\n\t\t} else {\n\t\t\tint hr = hourBox.getSelectedIndex() + 1;\n\t\t\tif (hr == 12)\n\t\t\t\thr = 0;\n\t\t\tString val = (String) ampmBox.getSelectedItem();\n\t\t\tif (val.equals(\"PM\"))\n\t\t\t\thr += 12;\n\t\t\tcal.set(Calendar.HOUR_OF_DAY, hr);\n\n\t\t}\n\n\t\ttry {\n\t\t\tint min = Integer.parseInt((String) minuteBox.getSelectedItem());\n\t\t\tif( min < 0 || min > 59 )\n\t\t\t\tthrow new Warning(Resource.getResourceString(\"InvalidMinute\")\n\t\t\t\t\t\t+ \": \" + minuteBox.getSelectedItem());\n\t\t\tcal.set(Calendar.MINUTE, min);\n\t\t} catch (NumberFormatException n) {\n\t\t\tthrow new Warning(Resource.getResourceString(\"InvalidMinute\")\n\t\t\t\t\t+ \": \" + minuteBox.getSelectedItem());\n\t\t}\n\t\tcal.set(Calendar.SECOND, 0);\n\t\tcal.set(Calendar.MILLISECOND, 0);\n\n\t\treturn cal.getTime();\n\t}\n\n\t\n\tprivate void initialize() {\n\n\t\tthis.setLayout(new GridBagLayout());\n\n\t\tthis.add(dateChooser,\n\t\t\t\tGridBagConstraintsFactory.create(0, 0, GridBagConstraints.BOTH));\n\n\t\tGridBagConstraints gbc = GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0);\n\t\tgbc.insets = new Insets(0, 0, 0, 0);\n\n\t\tJPanel timePanel = new JPanel();\n\t\ttimePanel.add(hourBox, null);\n\t\tminuteBox.setModel(minModel);\n\t\tminuteBox.setEditable(true);\n\t\tComboBoxEditor editor = minuteBox.getEditor();\n\t\tJTextField textField = (JTextField) editor.getEditorComponent();\n\t\ttextField.setColumns(2);\n\t\ttimePanel.add(minuteBox, null);\n\t\tampmBox.setModel(ampmModel);\n\t\ttimePanel.add(ampmBox, null);\n\t\tthis.add(timePanel, gbc);\n\n\t\tupdate(showDate, milTime);\n\n\t}\n\n\t@Override\n\tpublic void setEnabled(boolean enabled) {\n\t\tsuper.setEnabled(enabled);\n\t\thourBox.setEnabled(enabled);\n\t\tminuteBox.setEnabled(enabled);\n\t\tampmBox.setEnabled(enabled);\n\t\tdateChooser.setEnabled(enabled);\n\n\t}\n\n\t/**\n\t * Sets the date and time shown in the panel.\n\t * \n\t * @param d\n\t *            the new time\n\t */\n\tpublic void setTime(Date d) {\n\t\tCalendar cal = new GregorianCalendar();\n\t\tif (d != null)\n\t\t\tcal.setTime(d);\n\t\tdateChooser.setCalendar(cal);\n\t\tif (milTime) {\n\t\t\thourBox.setSelectedIndex(cal.get(Calendar.HOUR_OF_DAY));\n\t\t} else {\n\t\t\tint hr = cal.get(Calendar.HOUR);\n\t\t\tif (hr == 0)\n\t\t\t\thr = 12;\n\t\t\thourBox.setSelectedIndex(hr - 1);\n\t\t}\n\t\tString mins = Integer.toString(cal.get(Calendar.MINUTE));\n\t\tif (mins.length() == 1)\n\t\t\tmins = \"0\" + mins;\n\t\tminuteBox.setSelectedItem(mins);\n\t\tif (cal.get(Calendar.HOUR_OF_DAY) < 12)\n\t\t\tampmBox.setSelectedItem(\"AM\");\n\t\telse\n\t\t\tampmBox.setSelectedItem(\"PM\");\n\n\t}\n\n\t/**\n\t * update to reflect the settings of show date and military time\n\t */\n\tpublic void update(boolean show_date, boolean mil_Time) {\n\t\tif (mil_Time) {\n\t\t\thourBox.setModel(milHourModel);\n\t\t\tampmBox.setVisible(false);\n\t\t} else {\n\t\t\thourBox.setModel(normHourModel);\n\t\t\tampmBox.setVisible(true);\n\t\t}\n\n\t\tdateChooser.setVisible(show_date);\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/DynamicHTMLEditorKit.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport javax.swing.text.html.HTMLEditorKit;\nimport java.awt.event.MouseListener;\nimport java.awt.event.MouseMotionListener;\n\n\npublic class DynamicHTMLEditorKit extends HTMLEditorKit {\n\t\n\t\n\tprivate static final long serialVersionUID = 1L;\n\tprivate final HTMLLinkController linkController = new HTMLLinkController();\n\t\n    @Override\n\tpublic void install(JEditorPane c) {    \t\n        MouseListener[] oldMouseListeners = c.getMouseListeners();\n        MouseMotionListener[] oldMouseMotionListeners = c.getMouseMotionListeners();\n        \n        super.install(c);\n\n        for (MouseListener l: c.getMouseListeners()) {\n            c.removeMouseListener(l);\n        }\n        for (MouseListener l: oldMouseListeners) {\n            c.addMouseListener(l);\n        }\n\n        for (MouseMotionListener l: c.getMouseMotionListeners()) {\n            c.removeMouseMotionListener(l);\n        }\n        for (MouseMotionListener l: oldMouseMotionListeners) {\n            c.addMouseMotionListener(l);\n        }\n\n        c.addMouseListener(linkController);\n        c.addMouseMotionListener(linkController);\n    }\n    \n    public HTMLLinkController getHTMLLinkcontroller() {\n    \treturn linkController;\n    }\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/FileDrop.java",
    "content": "package net.sf.borg.ui.util;\n\nimport java.awt.datatransfer.DataFlavor;\nimport java.io.*;\n\n/**\n * This class makes it easy to drag and drop files from the operating system to\n * a Java program. Any <tt>java.awt.Component</tt> can be dropped onto, but only\n * <tt>javax.swing.JComponent</tt>s will indicate the drop event with a changed\n * border.\n * <p/>\n * To use this class, construct a new <tt>FileDrop</tt> by passing it the target\n * component and a <tt>Listener</tt> to receive notification when file(s) have\n * been dropped. Here is an example:\n * <p/>\n * <code><pre>\n *      JPanel myPanel = new JPanel();\n *      new FileDrop( myPanel, new FileDrop.Listener()\n *      {   public void filesDropped( java.io.File[] files )\n *          {   \n *              // handle file drop\n *              ...\n *          }   // end filesDropped\n *      }); // end FileDrop.Listener\n * </pre></code>\n * <p/>\n * You can specify the border that will appear when files are being dragged by\n * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only\n * <tt>JComponent</tt>s will show any indication with a border.\n * <p/>\n * You can turn on some debugging features by passing a <tt>PrintStream</tt>\n * object (such as <tt>System.out</tt>) into the full constructor. A\n * <tt>null</tt> value will result in no extra debugging information being\n * output.\n * <p/>\n *\n * <p>\n * I'm releasing this code into the Public Domain. Enjoy.\n * </p>\n * <p>\n * <em>Original author: Robert Harder, rharder@usa.net</em>\n * </p>\n * <p>\n * 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n * </p>\n *\n * @author Robert Harder\n * @author rharder@users.sf.net\n * @version 1.0.1\n */\n\n/*\n * modified from original to fix Eclipse warnings - mbb\n */\npublic class FileDrop {\n\tprivate transient javax.swing.border.Border normalBorder;\n\tprivate transient java.awt.dnd.DropTargetListener dropListener;\n\n\t/** Discover if the running JVM is modern enough to have drag and drop. */\n\tprivate static Boolean supportsDnD;\n\n\t// Default border color\n\tprivate static final java.awt.Color defaultBorderColor = new java.awt.Color(0f, 0f, 1f, 0.25f);\n\n\t/**\n\t * Constructs a {@link FileDrop} with a default light-blue border and, if\n\t * <var>c</var> is a {@link java.awt.Container}, recursively sets all elements\n\t * contained within as drop targets, though only the top level container will\n\t * change borders.\n\t *\n\t * @param c        Component on which files will be dropped.\n\t * @param listener Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.awt.Component c, final Listener listener) {\n\t\tthis(null, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tjavax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, defaultBorderColor), // Drag border\n\t\t\t\ttrue, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a default border and the option to recursively set drop\n\t * targets. If your component is a <tt>java.awt.Container</tt>, then each of its\n\t * children components will also listen for drops, though only the parent will\n\t * change borders.\n\t *\n\t * @param c         Component on which files will be dropped.\n\t * @param recursive Recursively set children as drop targets.\n\t * @param listener  Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.awt.Component c, final boolean recursive, final Listener listener) {\n\t\tthis(null, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tjavax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, defaultBorderColor), // Drag border\n\t\t\t\trecursive, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a default border and debugging optionally turned on. With\n\t * Debugging turned on, more status messages will be displayed to <tt>out</tt>.\n\t * A common way to use this constructor is with <tt>System.out</tt> or\n\t * <tt>System.err</tt>. A <tt>null</tt> value for the parameter <tt>out</tt>\n\t * will result in no debugging output.\n\t *\n\t * @param out      PrintStream to record debugging info or null for no\n\t *                 debugging.\n\t * @param out\n\t * @param c        Component on which files will be dropped.\n\t * @param listener Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.io.PrintStream out, final java.awt.Component c, final Listener listener) {\n\t\tthis(out, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tjavax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, defaultBorderColor), false, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a default border, debugging optionally turned on and the\n\t * option to recursively set drop targets. If your component is a\n\t * <tt>java.awt.Container</tt>, then each of its children components will also\n\t * listen for drops, though only the parent will change borders. With Debugging\n\t * turned on, more status messages will be displayed to <tt>out</tt>. A common\n\t * way to use this constructor is with <tt>System.out</tt> or\n\t * <tt>System.err</tt>. A <tt>null</tt> value for the parameter <tt>out</tt>\n\t * will result in no debugging output.\n\t *\n\t * @param out       PrintStream to record debugging info or null for no\n\t *                  debugging.\n\t * @param out\n\t * @param c         Component on which files will be dropped.\n\t * @param recursive Recursively set children as drop targets.\n\t * @param listener  Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.io.PrintStream out, final java.awt.Component c, final boolean recursive,\n\t\t\tfinal Listener listener) {\n\t\tthis(out, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tjavax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, defaultBorderColor), // Drag border\n\t\t\t\trecursive, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a specified border\n\t *\n\t * @param c          Component on which files will be dropped.\n\t * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.\n\t * @param listener   Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.awt.Component c, final javax.swing.border.Border dragBorder, final Listener listener) {\n\t\tthis(null, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tdragBorder, // Drag border\n\t\t\t\tfalse, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a specified border and the option to recursively set drop\n\t * targets. If your component is a <tt>java.awt.Container</tt>, then each of its\n\t * children components will also listen for drops, though only the parent will\n\t * change borders.\n\t *\n\t * @param c          Component on which files will be dropped.\n\t * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.\n\t * @param recursive  Recursively set children as drop targets.\n\t * @param listener   Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.awt.Component c, final javax.swing.border.Border dragBorder, final boolean recursive,\n\t\t\tfinal Listener listener) {\n\t\tthis(null, c, dragBorder, recursive, listener);\n\t} // end constructor\n\n\t/**\n\t * Constructor with a specified border and debugging optionally turned on. With\n\t * Debugging turned on, more status messages will be displayed to <tt>out</tt>.\n\t * A common way to use this constructor is with <tt>System.out</tt> or\n\t * <tt>System.err</tt>. A <tt>null</tt> value for the parameter <tt>out</tt>\n\t * will result in no debugging output.\n\t *\n\t * @param out        PrintStream to record debugging info or null for no\n\t *                   debugging.\n\t * @param c          Component on which files will be dropped.\n\t * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.\n\t * @param listener   Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.io.PrintStream out, final java.awt.Component c,\n\t\t\tfinal javax.swing.border.Border dragBorder, final Listener listener) {\n\t\tthis(out, // Logging stream\n\t\t\t\tc, // Drop target\n\t\t\t\tdragBorder, // Drag border\n\t\t\t\tfalse, // Recursive\n\t\t\t\tlistener);\n\t} // end constructor\n\n\t/**\n\t * Full constructor with a specified border and debugging optionally turned on.\n\t * With Debugging turned on, more status messages will be displayed to\n\t * <tt>out</tt>. A common way to use this constructor is with\n\t * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for the\n\t * parameter <tt>out</tt> will result in no debugging output.\n\t *\n\t * @param out        PrintStream to record debugging info or null for no\n\t *                   debugging.\n\t * @param c          Component on which files will be dropped.\n\t * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.\n\t * @param recursive  Recursively set children as drop targets.\n\t * @param listener   Listens for <tt>filesDropped</tt>.\n\t * @since 1.0\n\t */\n\tpublic FileDrop(final java.io.PrintStream out, final java.awt.Component c,\n\t\t\tfinal javax.swing.border.Border dragBorder, final boolean recursive, final Listener listener) {\n\n\t\tif (supportsDnD()) { // Make a drop listener\n\t\t\tdropListener = new java.awt.dnd.DropTargetListener() {\n\t\t\t\tpublic void dragEnter(java.awt.dnd.DropTargetDragEvent evt) {\n\t\t\t\t\tlog(out, \"FileDrop: dragEnter event.\");\n\n\t\t\t\t\t// Is this an acceptable drag event?\n\t\t\t\t\tif (isDragOk(out, evt)) {\n\t\t\t\t\t\t// If it's a Swing component, set its border\n\t\t\t\t\t\tif (c instanceof javax.swing.JComponent) {\n\t\t\t\t\t\t\tjavax.swing.JComponent jc = (javax.swing.JComponent) c;\n\t\t\t\t\t\t\tnormalBorder = jc.getBorder();\n\t\t\t\t\t\t\tlog(out, \"FileDrop: normal border saved.\");\n\t\t\t\t\t\t\tjc.setBorder(dragBorder);\n\t\t\t\t\t\t\tlog(out, \"FileDrop: drag border set.\");\n\t\t\t\t\t\t} // end if: JComponent\n\n\t\t\t\t\t\t// Acknowledge that it's okay to enter\n\t\t\t\t\t\t// evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );\n\t\t\t\t\t\tevt.acceptDrag(java.awt.dnd.DnDConstants.ACTION_COPY);\n\t\t\t\t\t\tlog(out, \"FileDrop: event accepted.\");\n\t\t\t\t\t} // end if: drag ok\n\t\t\t\t\telse { // Reject the drag event\n\t\t\t\t\t\tevt.rejectDrag();\n\t\t\t\t\t\tlog(out, \"FileDrop: event rejected.\");\n\t\t\t\t\t} // end else: drag not ok\n\t\t\t\t} // end dragEnter\n\n\t\t\t\tpublic void dragOver(java.awt.dnd.DropTargetDragEvent evt) { // This is called continually as long as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// the mouse is\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// over the drag target.\n\t\t\t\t} // end dragOver\n\n\t\t\t\tpublic void drop(java.awt.dnd.DropTargetDropEvent evt) {\n\t\t\t\t\tlog(out, \"FileDrop: drop event.\");\n\t\t\t\t\ttry { // Get whatever was dropped\n\t\t\t\t\t\tjava.awt.datatransfer.Transferable tr = evt.getTransferable();\n\n\t\t\t\t\t\t// Is it a file list?\n\t\t\t\t\t\tif (tr.isDataFlavorSupported(java.awt.datatransfer.DataFlavor.javaFileListFlavor)) {\n\t\t\t\t\t\t\t// Say we'll take it.\n\t\t\t\t\t\t\t// evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );\n\t\t\t\t\t\t\tevt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);\n\t\t\t\t\t\t\tlog(out, \"FileDrop: file list accepted.\");\n\n\t\t\t\t\t\t\t// Get a useful list\n\t\t\t\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\t\t\t\tjava.util.List<File> fileList = (java.util.List<File>) tr\n\t\t\t\t\t\t\t\t\t.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);\n\n\t\t\t\t\t\t\t// Convert list to array\n\t\t\t\t\t\t\tjava.io.File[] filesTemp = new java.io.File[fileList.size()];\n\t\t\t\t\t\t\tfileList.toArray(filesTemp);\n\t\t\t\t\t\t\tfinal java.io.File[] files = filesTemp;\n\n\t\t\t\t\t\t\t// Alert listener to drop.\n\t\t\t\t\t\t\tif (listener != null)\n\t\t\t\t\t\t\t\tlistener.filesDropped(files);\n\n\t\t\t\t\t\t\t// Mark that drop is completed.\n\t\t\t\t\t\t\tevt.getDropTargetContext().dropComplete(true);\n\t\t\t\t\t\t\tlog(out, \"FileDrop: drop complete.\");\n\t\t\t\t\t\t} // end if: file list\n\t\t\t\t\t\telse // this section will check for a reader flavor.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Thanks, Nathan!\n\t\t\t\t\t\t\t// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\t\t\t\t\t\t\tDataFlavor[] flavors = tr.getTransferDataFlavors();\n\t\t\t\t\t\t\tboolean handled = false;\n\t\t\t\t\t\t\tfor (int zz = 0; zz < flavors.length; zz++) {\n\t\t\t\t\t\t\t\tif (flavors[zz].isRepresentationClassReader()) {\n\t\t\t\t\t\t\t\t\t// Say we'll take it.\n\t\t\t\t\t\t\t\t\t// evt.acceptDrop ( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );\n\t\t\t\t\t\t\t\t\tevt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);\n\t\t\t\t\t\t\t\t\tlog(out, \"FileDrop: reader accepted.\");\n\n\t\t\t\t\t\t\t\t\tReader reader = flavors[zz].getReaderForText(tr);\n\n\t\t\t\t\t\t\t\t\tBufferedReader br = new BufferedReader(reader);\n\n\t\t\t\t\t\t\t\t\tif (listener != null)\n\t\t\t\t\t\t\t\t\t\tlistener.filesDropped(createFileArray(br, out));\n\n\t\t\t\t\t\t\t\t\t// Mark that drop is completed.\n\t\t\t\t\t\t\t\t\tevt.getDropTargetContext().dropComplete(true);\n\t\t\t\t\t\t\t\t\tlog(out, \"FileDrop: drop complete.\");\n\t\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!handled) {\n\t\t\t\t\t\t\t\tlog(out, \"FileDrop: not a file list or reader - abort.\");\n\t\t\t\t\t\t\t\tevt.rejectDrop();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\t\t\t\t\t\t} // end else: not a file list\n\t\t\t\t\t} // end try\n\t\t\t\t\tcatch (java.io.IOException io) {\n\t\t\t\t\t\tlog(out, \"FileDrop: IOException - abort:\");\n\t\t\t\t\t\tio.printStackTrace(out);\n\t\t\t\t\t\tevt.rejectDrop();\n\t\t\t\t\t} // end catch IOException\n\t\t\t\t\tcatch (java.awt.datatransfer.UnsupportedFlavorException ufe) {\n\t\t\t\t\t\tlog(out, \"FileDrop: UnsupportedFlavorException - abort:\");\n\t\t\t\t\t\tufe.printStackTrace(out);\n\t\t\t\t\t\tevt.rejectDrop();\n\t\t\t\t\t} // end catch: UnsupportedFlavorException\n\t\t\t\t\tfinally {\n\t\t\t\t\t\t// If it's a Swing component, reset its border\n\t\t\t\t\t\tif (c instanceof javax.swing.JComponent) {\n\t\t\t\t\t\t\tjavax.swing.JComponent jc = (javax.swing.JComponent) c;\n\t\t\t\t\t\t\tjc.setBorder(normalBorder);\n\t\t\t\t\t\t\tlog(out, \"FileDrop: normal border restored.\");\n\t\t\t\t\t\t} // end if: JComponent\n\t\t\t\t\t} // end finally\n\t\t\t\t} // end drop\n\n\t\t\t\tpublic void dragExit(java.awt.dnd.DropTargetEvent evt) {\n\t\t\t\t\tlog(out, \"FileDrop: dragExit event.\");\n\t\t\t\t\t// If it's a Swing component, reset its border\n\t\t\t\t\tif (c instanceof javax.swing.JComponent) {\n\t\t\t\t\t\tjavax.swing.JComponent jc = (javax.swing.JComponent) c;\n\t\t\t\t\t\tjc.setBorder(normalBorder);\n\t\t\t\t\t\tlog(out, \"FileDrop: normal border restored.\");\n\t\t\t\t\t} // end if: JComponent\n\t\t\t\t} // end dragExit\n\n\t\t\t\tpublic void dropActionChanged(java.awt.dnd.DropTargetDragEvent evt) {\n\t\t\t\t\tlog(out, \"FileDrop: dropActionChanged event.\");\n\t\t\t\t\t// Is this an acceptable drag event?\n\t\t\t\t\tif (isDragOk(out, evt)) { // evt.acceptDrag( java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );\n\t\t\t\t\t\tevt.acceptDrag(java.awt.dnd.DnDConstants.ACTION_COPY);\n\t\t\t\t\t\tlog(out, \"FileDrop: event accepted.\");\n\t\t\t\t\t} // end if: drag ok\n\t\t\t\t\telse {\n\t\t\t\t\t\tevt.rejectDrag();\n\t\t\t\t\t\tlog(out, \"FileDrop: event rejected.\");\n\t\t\t\t\t} // end else: drag not ok\n\t\t\t\t} // end dropActionChanged\n\t\t\t}; // end DropTargetListener\n\n\t\t\t// Make the component (and possibly children) drop targets\n\t\t\tmakeDropTarget(out, c, recursive);\n\t\t} // end if: supports dnd\n\t\telse {\n\t\t\tlog(out, \"FileDrop: Drag and drop is not supported with this JVM\");\n\t\t} // end else: does not support DnD\n\t} // end constructor\n\n\tprivate static boolean supportsDnD() { // Static Boolean\n\t\tif (supportsDnD == null) {\n\t\t\tboolean support = false;\n\t\t\ttry {\n\t\t\t\tClass.forName(\"java.awt.dnd.DnDConstants\");\n\t\t\t\tsupport = true;\n\t\t\t} // end try\n\t\t\tcatch (Exception e) {\n\t\t\t\tsupport = false;\n\t\t\t} // end catch\n\t\t\tsupportsDnD = support;\n\t\t} // end if: first time through\n\t\treturn supportsDnD.booleanValue();\n\t} // end supportsDnD\n\n\t// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\tprivate static final String ZERO_CHAR_STRING = \"\" + (char) 0;\n\n\tprivate static File[] createFileArray(BufferedReader bReader, PrintStream out) {\n\t\ttry {\n\t\t\tjava.util.List<File> list = new java.util.ArrayList<File>();\n\t\t\tjava.lang.String line = null;\n\t\t\twhile ((line = bReader.readLine()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\t// kde seems to append a 0 char to the end of the reader\n\t\t\t\t\tif (ZERO_CHAR_STRING.equals(line))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tjava.io.File file = new java.io.File(new java.net.URI(line));\n\t\t\t\t\tlist.add(file);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog(out, \"Error with \" + line + \": \" + ex.getMessage());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn list.toArray(new File[list.size()]);\n\t\t} catch (IOException ex) {\n\t\t\tlog(out, \"FileDrop: IOException\");\n\t\t}\n\t\treturn new File[0];\n\t}\n\t// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\n\tprivate void makeDropTarget(final java.io.PrintStream out, final java.awt.Component c, boolean recursive) {\n\t\t// Make drop target\n\t\tfinal java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();\n\t\ttry {\n\t\t\tdt.addDropTargetListener(dropListener);\n\t\t} // end try\n\t\tcatch (java.util.TooManyListenersException e) {\n\t\t\te.printStackTrace();\n\t\t\tlog(out, \"FileDrop: Drop will not work due to previous error. Do you have another listener attached?\");\n\t\t} // end catch\n\n\t\t// Listen for hierarchy changes and remove the drop target when the parent gets\n\t\t// cleared out.\n\t\tc.addHierarchyListener(new java.awt.event.HierarchyListener() {\n\t\t\tpublic void hierarchyChanged(java.awt.event.HierarchyEvent evt) {\n\t\t\t\tlog(out, \"FileDrop: Hierarchy changed.\");\n\t\t\t\tjava.awt.Component parent = c.getParent();\n\t\t\t\tif (parent == null) {\n\t\t\t\t\tc.setDropTarget(null);\n\t\t\t\t\tlog(out, \"FileDrop: Drop target cleared from component.\");\n\t\t\t\t} // end if: null parent\n\t\t\t\telse {\n\t\t\t\t\tnew java.awt.dnd.DropTarget(c, dropListener);\n\t\t\t\t\tlog(out, \"FileDrop: Drop target added to component.\");\n\t\t\t\t} // end else: parent not null\n\t\t\t} // end hierarchyChanged\n\t\t}); // end hierarchy listener\n\t\tif (c.getParent() != null)\n\t\t\tnew java.awt.dnd.DropTarget(c, dropListener);\n\n\t\tif (recursive && (c instanceof java.awt.Container)) {\n\t\t\t// Get the container\n\t\t\tjava.awt.Container cont = (java.awt.Container) c;\n\n\t\t\t// Get it's components\n\t\t\tjava.awt.Component[] comps = cont.getComponents();\n\n\t\t\t// Set it's components as listeners also\n\t\t\tfor (int i = 0; i < comps.length; i++)\n\t\t\t\tmakeDropTarget(out, comps[i], recursive);\n\t\t} // end if: recursively set components as listener\n\t} // end dropListener\n\n\t/** Determine if the dragged data is a file list. */\n\tprivate boolean isDragOk(final java.io.PrintStream out, final java.awt.dnd.DropTargetDragEvent evt) {\n\t\tboolean ok = false;\n\n\t\t// Get data flavors being dragged\n\t\tjava.awt.datatransfer.DataFlavor[] flavors = evt.getCurrentDataFlavors();\n\n\t\t// See if any of the flavors are a file list\n\t\tint i = 0;\n\t\twhile (!ok && i < flavors.length) {\n\t\t\t// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\t\t\t// Is the flavor a file list?\n\t\t\tfinal DataFlavor curFlavor = flavors[i];\n\t\t\tif (curFlavor.equals(java.awt.datatransfer.DataFlavor.javaFileListFlavor)\n\t\t\t\t\t|| curFlavor.isRepresentationClassReader()) {\n\t\t\t\tok = true;\n\t\t\t}\n\t\t\t// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.\n\t\t\ti++;\n\t\t} // end while: through flavors\n\n\t\t// If logging is enabled, show data flavors\n\t\tif (out != null) {\n\t\t\tif (flavors.length == 0)\n\t\t\t\tlog(out, \"FileDrop: no data flavors.\");\n\t\t\tfor (i = 0; i < flavors.length; i++)\n\t\t\t\tlog(out, flavors[i].toString());\n\t\t} // end if: logging enabled\n\n\t\treturn ok;\n\t} // end isDragOk\n\n\t/** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */\n\tprivate static void log(java.io.PrintStream out, String message) { // Log message if requested\n\t\tif (out != null)\n\t\t\tout.println(message);\n\t} // end log\n\n\t/**\n\t * Removes the drag-and-drop hooks from the component and optionally from the\n\t * all children. You should call this if you add and remove components after\n\t * you've set up the drag-and-drop. This will recursively unregister all\n\t * components contained within <var>c</var> if <var>c</var> is a\n\t * {@link java.awt.Container}.\n\t *\n\t * @param c The component to unregister as a drop target\n\t * @since 1.0\n\t */\n\tpublic static boolean remove(java.awt.Component c) {\n\t\treturn remove(null, c, true);\n\t} // end remove\n\n\t/**\n\t * Removes the drag-and-drop hooks from the component and optionally from the\n\t * all children. You should call this if you add and remove components after\n\t * you've set up the drag-and-drop.\n\t *\n\t * @param out       Optional {@link java.io.PrintStream} for logging drag and\n\t *                  drop messages\n\t * @param c         The component to unregister\n\t * @param recursive Recursively unregister components within a container\n\t * @since 1.0\n\t */\n\tpublic static boolean remove(java.io.PrintStream out, java.awt.Component c, boolean recursive) { // Make sure we\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// support dnd.\n\t\tif (supportsDnD()) {\n\t\t\tlog(out, \"FileDrop: Removing drag-and-drop hooks.\");\n\t\t\tc.setDropTarget(null);\n\t\t\tif (recursive && (c instanceof java.awt.Container)) {\n\t\t\t\tjava.awt.Component[] comps = ((java.awt.Container) c).getComponents();\n\t\t\t\tfor (int i = 0; i < comps.length; i++)\n\t\t\t\t\tremove(out, comps[i], recursive);\n\t\t\t\treturn true;\n\t\t\t} // end if: recursive\n\t\t\telse\n\t\t\t\treturn false;\n\t\t} // end if: supports DnD\n\t\telse\n\t\t\treturn false;\n\t} // end remove\n\n\t/* ******** I N N E R I N T E R F A C E L I S T E N E R ******** */\n\n\t/**\n\t * Implement this inner interface to listen for when files are dropped. For\n\t * example your class declaration may begin like this: <code><pre>\n\t *      public class MyClass implements FileDrop.Listener\n\t *      ...\n\t *      public void filesDropped( java.io.File[] files )\n\t *      {\n\t *          ...\n\t *      }   // end filesDropped\n\t *      ...\n\t * </pre></code>\n\t *\n\t * @since 1.1\n\t */\n\tpublic interface Listener {\n\n\t\t/**\n\t\t * This method is called when files have been successfully dropped.\n\t\t *\n\t\t * @param files An array of <tt>File</tt>s that were dropped.\n\t\t * @since 1.0\n\t\t */\n\t\tvoid filesDropped(java.io.File[] files);\n\n\t} // end inner-interface Listener\n\n\t/* ******** I N N E R C L A S S ******** */\n\n\t/**\n\t * This is the event that is passed to the {@link FileDropListener#filesDropped\n\t * filesDropped(...)} method in your {@link FileDropListener} when files are\n\t * dropped onto a registered drop target.\n\t *\n\t * <p>\n\t * I'm releasing this code into the Public Domain. Enjoy.\n\t * </p>\n\t * \n\t * @author Robert Harder\n\t * @author rob@iharder.net\n\t * @version 1.2\n\t */\n\tpublic static class Event extends java.util.EventObject {\n\n\t\t/**\n\t\t * \n\t\t */\n\t\tprivate static final long serialVersionUID = 5528693260511825706L;\n\t\tprivate final java.io.File[] files;\n\n\t\t/**\n\t\t * Constructs an {@link Event} with the array of files that were dropped and the\n\t\t * {@link FileDrop} that initiated the event.\n\t\t *\n\t\t * @param files The array of files that were dropped\n\t\t * @source The event source\n\t\t * @since 1.1\n\t\t */\n\t\tpublic Event(java.io.File[] files, Object source) {\n\t\t\tsuper(source);\n\t\t\tthis.files = files;\n\t\t} // end constructor\n\n\t\t/**\n\t\t * Returns an array of files that were dropped on a registered drop target.\n\t\t *\n\t\t * @return array of files that were dropped\n\t\t * @since 1.1\n\t\t */\n\t\tpublic java.io.File[] getFiles() {\n\t\t\treturn files;\n\t\t} // end getFiles\n\n\t} // end inner class Event\n\n\t/* ******** I N N E R C L A S S ******** */\n\n\t/**\n\t * At last an easy way to encapsulate your custom objects for dragging and\n\t * dropping in your Java programs! When you need to create a\n\t * {@link java.awt.datatransfer.Transferable} object, use this class to wrap\n\t * your object. For example:\n\t * \n\t * <pre>\n\t * <code>\n\t *      ...\n\t *      MyCoolClass myObj = new MyCoolClass();\n\t *      Transferable xfer = new TransferableObject( myObj );\n\t *      ...\n\t * </code>\n\t * </pre>\n\t * \n\t * Or if you need to know when the data was actually dropped, like when you're\n\t * moving data out of a list, say, you can use the\n\t * {@link TransferableObject.Fetcher} inner class to return your object Just in\n\t * Time. For example:\n\t * \n\t * <pre>\n\t * <code>\n\t *      ...\n\t *      final MyCoolClass myObj = new MyCoolClass();\n\t *\n\t *      TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()\n\t *      {   public Object getObject(){ return myObj; }\n\t *      }; // end fetcher\n\t *\n\t *      Transferable xfer = new TransferableObject( fetcher );\n\t *      ...\n\t * </code>\n\t * </pre>\n\t *\n\t * The {@link java.awt.datatransfer.DataFlavor} associated with\n\t * {@link TransferableObject} has the representation class\n\t * <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type\n\t * <tt>application/x-net.iharder.dnd.TransferableObject</tt>. This data flavor\n\t * is accessible via the static {@link #DATA_FLAVOR} property.\n\t *\n\t *\n\t * <p>\n\t * I'm releasing this code into the Public Domain. Enjoy.\n\t * </p>\n\t * \n\t * @author Robert Harder\n\t * @author rob@iharder.net\n\t * @version 1.2\n\t */\n\tpublic static class TransferableObject implements java.awt.datatransfer.Transferable {\n\t\t/**\n\t\t * The MIME type for {@link #DATA_FLAVOR} is\n\t\t * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.\n\t\t *\n\t\t * @since 1.1\n\t\t */\n\t\tpublic final static String MIME_TYPE = \"application/x-net.iharder.dnd.TransferableObject\";\n\n\t\t/**\n\t\t * The default {@link java.awt.datatransfer.DataFlavor} for\n\t\t * {@link TransferableObject} has the representation class\n\t\t * <tt>net.iharder.dnd.TransferableObject.class</tt> and the MIME type\n\t\t * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.\n\t\t *\n\t\t * @since 1.1\n\t\t */\n\t\tpublic final static java.awt.datatransfer.DataFlavor DATA_FLAVOR = new java.awt.datatransfer.DataFlavor(\n\t\t\t\tFileDrop.TransferableObject.class, MIME_TYPE);\n\n\t\tprivate Fetcher fetcher;\n\t\tprivate Object data;\n\n\t\tprivate java.awt.datatransfer.DataFlavor customFlavor;\n\n\t\t/**\n\t\t * Creates a new {@link TransferableObject} that wraps <var>data</var>. Along\n\t\t * with the {@link #DATA_FLAVOR} associated with this class, this creates a\n\t\t * custom data flavor with a representation class determined from\n\t\t * <code>data.getClass()</code> and the MIME type\n\t\t * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.\n\t\t *\n\t\t * @param data The data to transfer\n\t\t * @since 1.1\n\t\t */\n\t\tpublic TransferableObject(Object data) {\n\t\t\tthis.data = data;\n\t\t\tthis.customFlavor = new java.awt.datatransfer.DataFlavor(data.getClass(), MIME_TYPE);\n\t\t} // end constructor\n\n\t\t/**\n\t\t * Creates a new {@link TransferableObject} that will return the object that is\n\t\t * returned by <var>fetcher</var>. No custom data flavor is set other than the\n\t\t * default {@link #DATA_FLAVOR}.\n\t\t *\n\t\t * @see Fetcher\n\t\t * @param fetcher The {@link Fetcher} that will return the data object\n\t\t * @since 1.1\n\t\t */\n\t\tpublic TransferableObject(Fetcher fetcher) {\n\t\t\tthis.fetcher = fetcher;\n\t\t} // end constructor\n\n\t\t/**\n\t\t * Creates a new {@link TransferableObject} that will return the object that is\n\t\t * returned by <var>fetcher</var>. Along with the {@link #DATA_FLAVOR}\n\t\t * associated with this class, this creates a custom data flavor with a\n\t\t * representation class <var>dataClass</var> and the MIME type\n\t\t * <tt>application/x-net.iharder.dnd.TransferableObject</tt>.\n\t\t *\n\t\t * @see Fetcher\n\t\t * @param dataClass The {@link java.lang.Class} to use in the custom data flavor\n\t\t * @param fetcher   The {@link Fetcher} that will return the data object\n\t\t * @since 1.1\n\t\t */\n\t\tpublic TransferableObject(Class<?> dataClass, Fetcher fetcher) {\n\t\t\tthis.fetcher = fetcher;\n\t\t\tthis.customFlavor = new java.awt.datatransfer.DataFlavor(dataClass, MIME_TYPE);\n\t\t} // end constructor\n\n\t\t/**\n\t\t * Returns the custom {@link java.awt.datatransfer.DataFlavor} associated with\n\t\t * the encapsulated object or <tt>null</tt> if the {@link Fetcher} constructor\n\t\t * was used without passing a {@link java.lang.Class}.\n\t\t *\n\t\t * @return The custom data flavor for the encapsulated object\n\t\t * @since 1.1\n\t\t */\n\t\tpublic java.awt.datatransfer.DataFlavor getCustomDataFlavor() {\n\t\t\treturn customFlavor;\n\t\t} // end getCustomDataFlavor\n\n\t\t/* ******** T R A N S F E R A B L E M E T H O D S ******** */\n\n\t\t/**\n\t\t * Returns a two- or three-element array containing first the custom data\n\t\t * flavor, if one was created in the constructors, second the default\n\t\t * {@link #DATA_FLAVOR} associated with {@link TransferableObject}, and third\n\t\t * the {@link java.awt.datatransfer.DataFlavor.stringFlavor}.\n\t\t *\n\t\t * @return An array of supported data flavors\n\t\t * @since 1.1\n\t\t */\n\t\tpublic java.awt.datatransfer.DataFlavor[] getTransferDataFlavors() {\n\t\t\tif (customFlavor != null)\n\t\t\t\treturn new java.awt.datatransfer.DataFlavor[] { customFlavor, DATA_FLAVOR,\n\t\t\t\t\t\tjava.awt.datatransfer.DataFlavor.stringFlavor }; // end flavors array\n\t\t\telse\n\t\t\t\treturn new java.awt.datatransfer.DataFlavor[] { DATA_FLAVOR,\n\t\t\t\t\t\tjava.awt.datatransfer.DataFlavor.stringFlavor }; // end flavors array\n\t\t} // end getTransferDataFlavors\n\n\t\t/**\n\t\t * Returns the data encapsulated in this {@link TransferableObject}. If the\n\t\t * {@link Fetcher} constructor was used, then this is when the\n\t\t * {@link Fetcher#getObject getObject()} method will be called. If the requested\n\t\t * data flavor is not supported, then the {@link Fetcher#getObject getObject()}\n\t\t * method will not be called.\n\t\t *\n\t\t * @param flavor The data flavor for the data to return\n\t\t * @return The dropped data\n\t\t * @since 1.1\n\t\t */\n\t\tpublic Object getTransferData(java.awt.datatransfer.DataFlavor flavor)\n\t\t\t\tthrows java.awt.datatransfer.UnsupportedFlavorException, java.io.IOException {\n\t\t\t// Native object\n\t\t\tif (flavor.equals(DATA_FLAVOR))\n\t\t\t\treturn fetcher == null ? data : fetcher.getObject();\n\n\t\t\t// String\n\t\t\tif (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))\n\t\t\t\treturn fetcher == null ? data.toString() : fetcher.getObject().toString();\n\n\t\t\t// We can't do anything else\n\t\t\tthrow new java.awt.datatransfer.UnsupportedFlavorException(flavor);\n\t\t} // end getTransferData\n\n\t\t/**\n\t\t * Returns <tt>true</tt> if <var>flavor</var> is one of the supported flavors.\n\t\t * Flavors are supported using the <code>equals(...)</code> method.\n\t\t *\n\t\t * @param flavor The data flavor to check\n\t\t * @return Whether or not the flavor is supported\n\t\t * @since 1.1\n\t\t */\n\t\tpublic boolean isDataFlavorSupported(java.awt.datatransfer.DataFlavor flavor) {\n\t\t\t// Native object\n\t\t\tif (flavor.equals(DATA_FLAVOR))\n\t\t\t\treturn true;\n\n\t\t\t// String\n\t\t\treturn flavor.equals(DataFlavor.stringFlavor);\n\n\t\t\t// We can't do anything else\n\t\t} // end isDataFlavorSupported\n\n\t\t/* ******** I N N E R I N T E R F A C E F E T C H E R ******** */\n\n\t\t/**\n\t\t * Instead of passing your data directly to the {@link TransferableObject}\n\t\t * constructor, you may want to know exactly when your data was received in case\n\t\t * you need to remove it from its source (or do anyting else to it). When the\n\t\t * {@link #getTransferData getTransferData(...)} method is called on the\n\t\t * {@link TransferableObject}, the {@link Fetcher}'s {@link #getObject\n\t\t * getObject()} method will be called.\n\t\t *\n\t\t * @author Robert Harder\n\t\t * @copyright 2001\n\t\t * @version 1.1\n\t\t * @since 1.1\n\t\t */\n\t\tpublic interface Fetcher {\n\t\t\t/**\n\t\t\t * Return the object being encapsulated in the {@link TransferableObject}.\n\t\t\t *\n\t\t\t * @return The dropped object\n\t\t\t * @since 1.1\n\t\t\t */\n\t\t\tObject getObject();\n\t\t} // end inner interface Fetcher\n\n\t} // end class TransferableObject\n\n} // end class FileDrop\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/GridBagConstraintsFactory.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2008 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\nimport java.awt.*;\n\n/**\n * A factory for creating GridBagConstraints objects in a standard way. Saves lots\n * of LOC.\n */\npublic class GridBagConstraintsFactory {\n\n\tstatic private final Insets defaultInsets = new Insets(4, 4, 4, 4);\n\n\t/**\n\t * Creates GridBagConstraints\n\t * \n\t * @param x the x\n\t * @param y the y\n\t * \n\t * @return the grid bag constraints\n\t */\n\tpublic static GridBagConstraints create(int x, int y) {\n\t\tGridBagConstraints gbc = new GridBagConstraints();\n\t\tgbc.gridx = x;\n\t\tgbc.gridy = y;\n\t\tgbc.insets = defaultInsets;\n\t\treturn gbc;\n\t}\n\n\t/**\n\t * Creates GridBagConstraints\n\t * \n\t * @param x the x\n\t * @param y the y\n\t * @param fill the fill\n\t * \n\t * @return the grid bag constraints\n\t */\n\tpublic static GridBagConstraints create(int x, int y, int fill) {\n\t\tGridBagConstraints gbc = create(x, y);\n\t\tgbc.fill = fill;\n\t\treturn gbc;\n\t}\n\n\t/**\n\t * Creates GridBagConstraints\n\t * \n\t * @param x the x\n\t * @param y the y\n\t * @param fill the fill\n\t * @param weightx the weightx\n\t * @param weighty the weighty\n\t * \n\t * @return the grid bag constraints\n\t */\n\tpublic static GridBagConstraints create(int x, int y, int fill,\n\t\t\tdouble weightx, double weighty) {\n\t\tGridBagConstraints gbc = create(x, y, fill);\n\t\tgbc.weightx = weightx;\n\t\tgbc.weighty = weighty;\n\t\treturn gbc;\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/HTMLDocumentListener.java",
    "content": "package net.sf.borg.ui.util;\n\nimport net.sf.borg.common.Errmsg;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentEvent.EventType;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Element;\nimport javax.swing.text.SimpleAttributeSet;\nimport javax.swing.text.StyleConstants;\nimport javax.swing.text.html.HTML;\nimport javax.swing.text.html.HTMLDocument;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class HTMLDocumentListener implements DocumentListener {\n\n\tprivate boolean initializingDoc;\n\n\tprivate final JEditorPane textPane;\n\n\tprivate final List<HTMLHyperlinkRange> hyperlinkList;\n\n\tpublic static final String linkRegex = \"\\\\b(((ht|f)tp(s?)\\\\:\\\\/\\\\/|~\\\\/|\\\\/))\"\n\t\t\t+ \"(\\\\w+:\\\\w+@)?(([-\\\\w]+\\\\.)+(com|org|net|gov\"\n\t\t\t+ \"|mil|biz|info|mobi|name|aero|jobs|museum|edu\"\n\t\t\t+ \"|travel|[a-z]{2}))(:[\\\\d]{1,5})?\"\n\t\t\t+ \"(((\\\\/([-\\\\w~!$+|.,=]|%[a-f\\\\d]{2})+)+|\\\\/)+|\\\\?|#)?\"\n\t\t\t+ \"((\\\\?([-\\\\w~!$+|.,*:]|%[a-f\\\\d{2}])+=?\"\n\t\t\t+ \"([-\\\\w~!$+|.,*:=]|%[a-f\\\\d]{2})*)\"\n\t\t\t+ \"(&(?:[-\\\\w~!$+|.,*:]|%[a-f\\\\d{2}])+=?\"\n\t\t\t+ \"([-\\\\w~!$+|.,*:=]|%[a-f\\\\d]{2})*)*)*\"\n\t\t\t+ \"(#([-\\\\w~!$+|.,*:=]|%[a-f\\\\d]{2})*)?\\\\b\";\n\t\n\tprivate final Pattern pattern = Pattern.compile(linkRegex);\n\n\tpublic HTMLDocumentListener(JEditorPane newTextPane) {\n\t\thyperlinkList = new ArrayList<HTMLHyperlinkRange>();\n\t\ttextPane = newTextPane;\n\t\tinitializingDoc = false;\n\t}\n\n\tprivate boolean isExistingLink(int offset) {\n\t\tfor (HTMLHyperlinkRange hl : hyperlinkList) {\n\t\t\tif (hl.getStart() == offset)\n\t\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate boolean isChangeInHyperlink(DocumentEvent evt) {\n\t\tfor (HTMLHyperlinkRange hl : hyperlinkList) {\n\t\t\tif (hl.isInRange(evt.getOffset())\n\t\t\t\t\t&& hl.isInRange(textPane.getCaretPosition()))\n\t\t\t\treturn true;\n\n\t\t\tif (evt.getType() == EventType.INSERT\n\t\t\t\t\t&& evt.getOffset() == hl.getEnd())\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void addToHyperlinkList(HTMLHyperlinkRange link) {\n\t\thyperlinkList.add(link);\n\t}\n\n\tprivate void removeFromHyperlinkList(HTMLHyperlinkRange linkRange) {\n\t\thyperlinkList.remove(linkRange);\n\t}\n\n\tprivate void updateHyperlinkList(int offset, int len) {\n\t\tfor (HTMLHyperlinkRange hl : hyperlinkList) {\n\t\t\tif (hl.getStart() < offset)\n\t\t\t\tcontinue;\n\t\t\thl.setStart(hl.getStart() + len);\n\t\t}\n\t}\n\n\tpublic void resetHyperlinkList(HTMLDocument doc) {\n\t\thyperlinkList.clear();\n\t\tcheckForHyperlinks(doc);\n\t}\n\n\tprivate HTMLHyperlinkRange getHyperlinkRange(int start) {\n\t\tfor (HTMLHyperlinkRange hl : hyperlinkList)\n\t\t\tif (start >= hl.getStart() && start <= hl.getEnd())\n\t\t\t\treturn hl;\n\n\t\treturn null;\n\t}\n\n\tprivate void createHyperlink(HTMLDocument doc, int offset, String url) {\n\t\tSimpleAttributeSet a = new SimpleAttributeSet();\n\t\ta.addAttribute(\"DUMMY_ATTRIBUTE_NAME\", \"DUMMY_ATTRIBUTE_VALUE\");\n\t\tdoc.setCharacterAttributes(offset, url.length(), a, false);\n\n\t\tElement elem = doc.getCharacterElement(offset);\n\t\tString html = \"<a href='\" + url + \"'>\" + url + \"</a>\";\n\n\t\ttry {\n\t\t\tdoc.setOuterHTML(elem, html);\n\t\t\taddToHyperlinkList(new HTMLHyperlinkRange(offset, url.length()));\n\t\t} catch (BadLocationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tprivate void invalidateHyperlink(HTMLDocument doc, DocumentEvent evt,\n\t\t\tboolean wasEnter) {\n\t\tHTMLHyperlinkRange linkRange = getHyperlinkRange(evt.getOffset());\n\n\t\tElement element = doc.getCharacterElement(linkRange.getStart());\n\t\treplaceLinkElementWithText(doc, element);\n\n\t\t// Pressing Enter is weird, it splits a hyperlink into two, so we have\n\t\t// to\n\t\t// invalidate the second one as well.\n\t\tif (wasEnter) {\n\t\t\treplaceLinkElementWithText(doc,\n\t\t\t\t\tdoc.getCharacterElement(textPane.getCaretPosition()));\n\t\t}\n\n\t\tremoveFromHyperlinkList(linkRange);\n\t}\n\n\tprivate void checkForHyperlinks(HTMLDocument doc) {\n\t\tMatcher matcher;\n\t\ttry {\n\t\t\tmatcher = pattern.matcher(\n\t\t\t\t\tdoc.getText(0, doc.getLength()));\n\n\t\t\twhile (matcher.find())\n\t\t\t\tif (!isExistingLink(matcher.start()))\n\t\t\t\t\tcreateHyperlink(doc, matcher.start(), matcher.group());\n\n\t\t} catch (BadLocationException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic void setInitializingDoc(boolean newState) {\n\t\tinitializingDoc = newState;\n\t}\n\n\t@Override\n\tpublic void insertUpdate(DocumentEvent evt) {\n\t\tfinal DocumentEvent e = evt;\n\n\t\t// We always want to update the hyperlink ranges\n\t\tupdateHyperlinkList(e.getOffset(), e.getLength());\n\n\t\t// Ignore some document change events when initializing a doc\n\t\tif (initializingDoc)\n\t\t\treturn;\n\n\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tif (e.getDocument() instanceof HTMLDocument\n\t\t\t\t\t\t&& e.getType() != EventType.CHANGE) {\n\t\t\t\t\tHTMLDocument doc = (HTMLDocument) e.getDocument();\n\t\t\t\t\tString text = \"\";\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttext = doc.getText(e.getOffset(), e.getLength());\n\t\t\t\t\t} catch (BadLocationException e1) {\n\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isChangeInHyperlink(e))\n\t\t\t\t\t\tinvalidateHyperlink(doc, e, text.charAt(0) == '\\n');\n\t\t\t\t\telse if (e.getLength() == 1) {\n\t\t\t\t\t\tif (text.charAt(0) == ' ' || text.charAt(0) == '\\n'\n\t\t\t\t\t\t\t\t|| text.charAt(0) == '\\t') {\n\t\t\t\t\t\t\tcheckForHyperlinks(doc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void changedUpdate(DocumentEvent evt) {\n\t\t// empty\n\t}\n\n\t@Override\n\tpublic void removeUpdate(DocumentEvent evt) {\n\t\tfinal DocumentEvent e = evt;\n\n\t\t// We always want to update the hyperlink ranges\n\t\tupdateHyperlinkList(e.getOffset(), -e.getLength());\n\n\t\t// Ignore some document change events when initializing a doc\n\t\tif (initializingDoc)\n\t\t\treturn;\n\n\t\tSwingUtilities.invokeLater(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tif (e.getDocument() instanceof HTMLDocument) {\n\t\t\t\t\tHTMLDocument doc = (HTMLDocument) e.getDocument();\n\n\t\t\t\t\tif (isChangeInHyperlink(e))\n\t\t\t\t\t\tinvalidateHyperlink(doc, e, false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static void replaceLinkElementWithText(HTMLDocument doc, Element element) {\n\t\tString plaintext = null;\n\n\t\ttry {\n\t\t\tObject tag = element.getAttributes().getAttribute(\n\t\t\t\t\tStyleConstants.NameAttribute);\n\t\t\tif (tag != null && tag == HTML.Tag.CONTENT) {\n\t\t\t\tint startOffset = element.getStartOffset();\n\t\t\t\tint endOffset = element.getEndOffset();\n\t\t\t\tint length = endOffset - startOffset;\n\t\t\t\tplaintext = doc.getText(startOffset, length).trim();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\treturn;\n\t\t}\n\n\t\tif (plaintext != null) {\n\t\t\ttry {\n\t\t\t\tdoc.setOuterHTML(element, plaintext);\n\t\t\t} catch (Exception e) {\n\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t}\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/HTMLHyperlinkRange.java",
    "content": "package net.sf.borg.ui.util;\n\npublic class HTMLHyperlinkRange {\n\tprivate int start;\n\tprivate int length;\n\t\n\tpublic HTMLHyperlinkRange(int s, int len) {\n\t\tsetStart(s);\n\t\tsetLength(len);\n\t}\n\t\n\tpublic int getStart() {\n\t\treturn start;\n\t}\n\t\n\tpublic int getEnd() {\n\t\treturn start + length;\n\t}\n\t\n\tpublic int getLength() {\n\t\treturn length;\n\t}\n\t\n\tpublic void setStart(int newStart) {\n\t\tstart = newStart;\n\t}\n\t\n\tpublic void setLength(int newLength) {\n\t\tlength = newLength;\n\t}\n\t\n\tpublic boolean isInRange(int offset) {\n\t\treturn (offset >= getStart() && offset <= getEnd());\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/HTMLLimitDocument.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.text.AttributeSet;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.html.HTMLDocument;\nimport java.awt.*;\n\npublic class HTMLLimitDocument extends HTMLDocument {\n\tprivate static final long serialVersionUID = 1L;\n\tint maxLength; // max characters allowed\n\n\tpublic void setMaxLength(int newMaxLength) {\n\t\tmaxLength = newMaxLength;\n\t}\n\n\t@Override\n\tpublic void insertString(int offs, String str, AttributeSet attr)\n\t\t\tthrows BadLocationException {\n\t\tif (getLength() + str.length() > this.maxLength) {\n\t\t\tToolkit.getDefaultToolkit().beep();\n\t\t} else {\n\t\t\tsuper.insertString(offs, str, attr);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/HTMLLinkController.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport javax.swing.text.html.HTMLEditorKit.LinkController;\nimport java.awt.event.MouseEvent;\n\n\npublic class HTMLLinkController extends LinkController {\n\t\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate boolean needsCursorChange;\n\t\n\tpublic HTMLLinkController() {\n\t\tneedsCursorChange = true;\n\t}\n\t\n\tpublic boolean getNeedsCursorChange() {\n\t\treturn needsCursorChange;\n\t}\n\t\n\t@Override\n\tpublic void mouseClicked(MouseEvent e) {\n        JTextPane textPane = (JTextPane) e.getSource();\n\n        if (textPane.isEditable() &&\n        \tSwingUtilities.isLeftMouseButton(e) &&\n        \te.getClickCount() == 1) {\n            textPane.setEditable(false);\n            super.mouseClicked(e);\n            textPane.setEditable(true);\n        }\n\n    }\n\t\n    @Override\n\tpublic void mouseMoved(MouseEvent e) {\n    \tJTextPane textPane = (JTextPane) e.getSource();\n\n        if (textPane.isEditable()) {\n        \tneedsCursorChange = false;\n            textPane.setEditable(false);\n            needsCursorChange = true;\n            super.mouseMoved(e);\n            needsCursorChange = false;\n            textPane.setEditable(true);\n            needsCursorChange = true;\n        }\n    }\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/HTMLTextPane.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport javax.swing.event.HyperlinkEvent;\nimport javax.swing.event.HyperlinkListener;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Document;\nimport javax.swing.text.html.HTMLDocument;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.io.IOException;\nimport java.net.URI;\n\n\npublic class HTMLTextPane extends JTextPane {\n\t\n\tprivate static final long serialVersionUID = 1L;\n\tprivate DynamicHTMLEditorKit kit;\n\tprivate HTMLDocumentListener docListener;\n\t\n\tpublic HTMLTextPane() {\n\t\tinitComponent();\n\t}\n\t\n\tpublic HTMLTextPane(Document doc) {\n\t\tsetDocument(doc);\n\t\tinitComponent();\n\t}\n\t\n\tpublic void initComponent() {\n\t\tkit = new DynamicHTMLEditorKit();\n\t\tdocListener = new HTMLDocumentListener(this);\n\t\t\n\t\tsetEditorKit(kit);\n        setContentType(\"text/html\");\n        setTrueFont(UIManager.getFont(\"Label.font\"));\n        \n        getDocument().addDocumentListener(docListener);\n        \n        addHyperlinkListener(new HyperlinkListener() {\n            @Override\n\t\t\tpublic void hyperlinkUpdate(HyperlinkEvent evt) {\n                if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {\n                    String url = evt.getURL().toString();\n                    try {\n\t\t\t\t\t\tDesktop.getDesktop().browse(URI.create(url));\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n                }\n            }\n        });\n        \n        addMouseListener(new MouseAdapter() {\n            @Override\n\t\t\tpublic void mouseClicked(MouseEvent evt) {\n            \tif( evt.getButton() == MouseEvent.BUTTON3)\n            \t{\n                    setCaretPosition(viewToModel2D(evt.getPoint()));\n            \t}\n            }\n        });\n\t}\n\t\n\t@Override\n\tpublic void setCursor(Cursor cursor) {\n        if (kit != null && kit.getHTMLLinkcontroller().getNeedsCursorChange()) {\n            super.setCursor(cursor);\n        }\n    }\n\t\n\t/**\n\t * This modifies the HTML content of the JEditorPane to display the desired font.\n\t * \n\t * @param font\n\t * \t\t\tA Font type\n\t */\n\tpublic void setTrueFont(Font font)\n\t{\n\t\tString bodyRule = \"body { font-family: \" + font.getFamily() + \"; \" +\n\t\t\t\t\"font-size: \" + font.getSize() + \"pt; }\";\n\t\t((HTMLDocument)getDocument()).getStyleSheet().addRule(bodyRule);\n\t\t//Sets the font for when the JEditorPane is switched to plain text.\n\t\tsetFont(font);\n\t}\n\t\n\tpublic String getPlainText() {\n\t\ttry {\n\t\t\treturn getDocument().getText(0, getDocument().getLength());\n\t\t} catch (BadLocationException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\treturn \"\";\n\t}\n\t\n\t@Override\n\tpublic void setText(String text) {\n\t\ttry {\n\t\t\tdocListener.setInitializingDoc(true);\n\t\t\tDocument doc = getDocument();\n\t\t\tdoc.remove(0, doc.getLength());\n\t\t\tdoc.insertString(0, text, null);\n\t\t\t\n\t\t\tif (doc instanceof HTMLDocument)\n\t\t\t    docListener.resetHyperlinkList((HTMLDocument)doc);\n\t\t\t\n\t\t\tdocListener.setInitializingDoc(false);\n\t\t\t\n\t\t} catch (BadLocationException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/IconHelper.java",
    "content": "package net.sf.borg.ui.util;\n\nimport java.awt.Image;\n\nimport javax.swing.ImageIcon;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\n\npublic class IconHelper {\n\t\n\tpublic static ImageIcon getIcon(String path) {\n\t\t\n\t\tint iconSize = Prefs.getIntPref(PrefName.ICONSIZE);\n\t\t\n\t\tImageIcon icon = new  ImageIcon(IconHelper.class.getResource(\n\t\t\t\tpath));\n\t\t\n\t\tif( iconSize == icon.getIconHeight()) return icon;\n\t\t\n\t\t// if not square - don't scale\n\t\tif( icon.getIconHeight() != icon.getIconWidth()) return icon;\n\t\t\n\t\tImage img = icon.getImage();\n\t\tImage resizeImage = img.getScaledInstance(iconSize, iconSize, java.awt.Image.SCALE_SMOOTH);\n\t\treturn new ImageIcon(resizeImage);\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/InputDialog.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\n/*\n * helpscrn.java\n *\n * Created on October 5, 2003, 8:55 AM\n */\n\npackage net.sf.borg.ui.util;\n\nimport net.sf.borg.common.Resource;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * modal dialog that prompts for input and can limit the input length\n */\npublic class InputDialog extends JDialog {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * result string returned by the dialog\n\t */\n\tstatic private String returnString = null;\n\n\t/**\n\t * Show an input dialog that prompts for input of limited length\n\t * @param message the prompt message\n\t * @param maxLength the max input length allowed\n\t * @return the input or null if cancelled\n\t */\n\tstatic public String show(String message, int maxLength) {\n\t\treturnString = null;\n\t\tnew InputDialog(message, maxLength).setVisible(true);\n\t\treturn returnString;\n\t}\n\n\t/** The input field. */\n\tprivate final JTextField inputText;\n\n\t/**\n\t * Instantiates a new Input Dialog\n\t * \n\t * @param s\n\t *            the message\n\t * @param maxLength\n\t *            maximum input length\n\t */\n\tprivate InputDialog(String s, int maxLength) {\n\t\t\n\t\t//setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\n\t\tsetTitle(\"BORG\");\n\t\t\n\t\tthis.setSize(165, 300);\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\n\t\t// the prompt\n\t\tJLabel message = new JLabel(s);\n\t\ttopPanel.add(message, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t// the input field\n\t\tinputText = new JTextField(new LimitDocument(maxLength), null, maxLength);\n\t\ttopPanel.add(inputText, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t// the ok and cancel buttons\n\t\tJPanel buttonPanel = new JPanel();\n\t\tJButton okButton = new JButton();\n\t\tokButton.setText(Resource.getResourceString(\"OK\"));\n\t\tokButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\treturnString = inputText.getText();\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(okButton, null);\n\t\tJButton cancelButton = new JButton();\n\t\tcancelButton.setText(Resource.getResourceString(\"Cancel\"));\n\t\tcancelButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(cancelButton, null);\n\t\ttopPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 2,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 0.0));\n\n\t\tthis.setContentPane(topPanel);\n\n\t\tpack();\n\n\t\t// position in center of screen\n\t\tDimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n\t\tDimension labelSize = this.getSize();\n\t\tsetLocation(screenSize.width / 2 - (labelSize.width / 2),\n\t\t\t\tscreenSize.height / 2 - (labelSize.height / 2));\n\t\t\n\t\tsetModal(true);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/JTabbedPaneWithCloseIcons.java",
    "content": "package net.sf.borg.ui.util;\n\n// This file was copied from a forum and was unlicensed.\n// It has been modified\n// BORG does not apply any copyright to this file.\n\nimport net.sf.borg.ui.DockableView;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.awt.event.MouseMotionListener;\n\n/**\n * A JTabbedPane with Close and Undock icons drawn in the tab.\n */\npublic class JTabbedPaneWithCloseIcons extends JTabbedPane implements\n\t\tMouseListener, MouseMotionListener {\n\n\t/**\n\t * this is the icon that actually contains the symbols for close and\n\t * optionally undock.\n\t */\n\tprivate class CloseTabIcon implements Icon {\n\n\t\t/**\n\t\t * parent component\n\t\t */\n\t\tprivate Component component;\n\n\t\t/**\n\t\t * is undock icon being shown\n\t\t */\n\t\tprivate final boolean undock;\n\n\t\t/** The width. */\n\t\tprivate final int width;\n\n\t\t/** The x_pos. */\n\t\tprivate int x_pos;\n\n\t\t/** The y_pos. */\n\t\tprivate int y_pos;\n\n\t\tprivate boolean isHighlighted = false;\n\n\t\t/**\n\t\t * constructor.\n\t\t * \n\t\t * @param ud\n\t\t *            if true, also include the undock image\n\t\t */\n\t\tpublic CloseTabIcon(boolean ud) {\n\t\t\tthis.undock = ud;\n\n\t\t\t// undock doubles the width\n\t\t\tif (undock)\n\t\t\t\twidth = 2 * ICON_WIDTH;\n\t\t\telse\n\t\t\t\twidth = ICON_WIDTH;\n\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconHeight()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconHeight() {\n\t\t\treturn ICON_WIDTH;\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#getIconWidth()\n\t\t */\n\t\t@Override\n\t\tpublic int getIconWidth() {\n\t\t\treturn width;\n\t\t}\n\n\t\t/**\n\t\t * Checks if mouse is on the delete icon\n\t\t * \n\t\t * @param e\n\t\t *            the mouse event\n\t\t * \n\t\t * @return true, if mouse is on the delete icon\n\t\t */\n\t\tpublic boolean isMouseOnDelete(int x, int y) {\n\n\t\t\tRectangle rect = new Rectangle(x_pos + 2, y_pos + 2, ICON_WIDTH - 4,\n\t\t\t\t\tICON_WIDTH - 4);\n\t\t\treturn rect.contains(x, y);\n\t\t}\n\n\t\t/**\n\t\t * check if the mouse is anywhere on the CloseTabIcon\n\t\t * \n\t\t * @param e\n\t\t *            the mouse event\n\t\t * \n\t\t * @return true, if mouse is on the CloseTabIcon icon\n\t\t */\n\t\tpublic boolean contains(int x, int y) {\n\t\t\tRectangle rect = new Rectangle(x_pos, y_pos, getIconWidth(),\n\t\t\t\t\tgetIconHeight());\n\t\t\treturn rect.contains(x, y);\n\t\t}\n\n\t\t/**\n\t\t * Checks if mouse is on the undock icon\n\t\t * \n\t\t * @param e\n\t\t *            the mouse event\n\t\t * \n\t\t * @return true, if mouse is on the undock icon\n\t\t */\n\t\tpublic boolean isMouseOnUndock(int x, int y) {\n\n\t\t\tif (!undock) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tRectangle rect = new Rectangle(x_pos + ICON_WIDTH + 2, y_pos + 2,\n\t\t\t\t\tICON_WIDTH - 4, ICON_WIDTH - 4);\n\t\t\treturn rect.contains(x, y);\n\t\t}\n\n\t\t/**\n\t\t * Paint the icon with optional red highlight\n\t\t * \n\t\t * @param highlightDelete\n\t\t *            if true, paint delete highlight\n\t\t * @param highlightUndock\n\t\t *            if true, paint undock highlight\n\t\t * @param g\n\t\t *            the Graphics to paint in\n\t\t */\n\t\tprivate void paintHighlight(boolean highlightDelete,\n\t\t\t\tboolean highlightUndock, Graphics g) {\n\n\t\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\t\n\t\t\tColor orig = g2.getColor();\n\n\t\t\t//g2.setColor(Color.black);\n\n\t\t\tisHighlighted = false;\n\n\t\t\tif (highlightDelete) {\n\t\t\t\tg2.setColor(Color.red);\n\t\t\t\tisHighlighted = true;\n\t\t\t}\n\n\t\t\t// draw the delete picture\n\t\t\tg2.drawLine(x_pos + 4, y_pos + 5, x_pos + ICON_WIDTH - 5, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 4);\n\t\t\tg2.drawLine(x_pos + 4, y_pos + 4, x_pos + ICON_WIDTH - 4, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 4);\n\t\t\tg2.drawLine(x_pos + 5, y_pos + 4, x_pos + ICON_WIDTH - 4, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 5);\n\t\t\tg2.drawLine(x_pos + ICON_WIDTH - 4, y_pos + 5, x_pos + 5, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 4);\n\t\t\tg2.drawLine(x_pos + ICON_WIDTH - 4, y_pos + 4, x_pos + 4, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 4);\n\t\t\tg2.drawLine(x_pos + ICON_WIDTH - 5, y_pos + 4, x_pos + 4, y_pos\n\t\t\t\t\t+ ICON_WIDTH - 5);\n\n\t\t\tif (undock) {\n\n\t\t\t\tg2.setColor(orig);\n\t\t\t\tif (highlightUndock) {\n\t\t\t\t\tg2.setColor(Color.red);\n\t\t\t\t\tisHighlighted = true;\n\t\t\t\t}\n\n\t\t\t\t// draw the undock picture\n\t\t\t\tg2.drawRect(x_pos + ICON_WIDTH + 4, y_pos + 4, ICON_WIDTH - 8,\n\t\t\t\t\t\tICON_WIDTH - 8);\n\t\t\t\tg2.drawRect(x_pos + ICON_WIDTH + 5, y_pos + 5, ICON_WIDTH - 10,\n\t\t\t\t\t\tICON_WIDTH - 10);\n\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Paint icon with hightlight\n\t\t * \n\t\t * @param e\n\t\t *            the mouse event\n\t\t */\n\t\tpublic void paintHighlight(MouseEvent e) {\n\t\t\tif (component == null)\n\t\t\t\treturn;\n\n\t\t\t// don't paint the icon due to a mouse event if the mouse is not on\n\t\t\t// it - except if the\n\t\t\t// icon is currently highlighted and needs to be unhighlighted\n\t\t\tif (!contains(e.getX(), e.getY())) {\n\t\t\t\tif (isHighlighted) {\n\t\t\t\t\tpaintHighlight(false, false, component.getGraphics());\n\t\t\t\t}\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tpaintHighlight(isMouseOnDelete(e.getX(), e.getY()),\n\t\t\t\t\tisMouseOnUndock(e.getX(), e.getY()),\n\t\t\t\t\tcomponent.getGraphics());\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see javax.swing.Icon#paintIcon(java.awt.Component,\n\t\t * java.awt.Graphics, int, int)\n\t\t */\n\t\t@Override\n\t\tpublic void paintIcon(Component c, Graphics g, int x, int y) {\n\t\t\tthis.x_pos = x;\n\t\t\tthis.y_pos = y;\n\t\t\tthis.component = c;\n\n\t\t\t/*\n\t\t\t * use the last known mouse position to decide if highlight is\n\t\t\t * needed\n\t\t\t */\n\t\t\tif (lastMouseEvent != null)\n\t\t\t\tpaintHighlight(\n\t\t\t\t\t\tisMouseOnDelete(lastMouseEvent.getX(),\n\t\t\t\t\t\t\t\tlastMouseEvent.getY()),\n\t\t\t\t\t\tisMouseOnUndock(lastMouseEvent.getX(),\n\t\t\t\t\t\t\t\tlastMouseEvent.getY()), g);\n\t\t\telse\n\t\t\t\tpaintHighlight(false, false, g);\n\t\t}\n\t}\n\n\t/** icon size. */\n\tstatic private final int ICON_WIDTH = 16;\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/*\n\t * save last mouse event so that we know where we are if the icon is asked\n\t * to repaint without the mouse moving\n\t */\n\tprivate MouseEvent lastMouseEvent;\n\n\t/**\n\t * Instantiates a new tabbed pane with close icons.\n\t */\n\tpublic JTabbedPaneWithCloseIcons() {\n\t\tsuper();\n\t\taddMouseListener(this);\n\t\tthis.addMouseMotionListener(this);\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see javax.swing.JTabbedPane#addTab(java.lang.String, java.awt.Component)\n\t */\n\t@Override\n\tpublic void addTab(String title, Component component) {\n\t\t// add the extra undock icon onyl if the component is undockable\n\t\tif (component instanceof DockableView)\n\t\t\tsuper.addTab(title, new CloseTabIcon(true), component);\n\t\telse\n\t\t\tsuper.addTab(title, new CloseTabIcon(false), component);\n\t\tthis.repaint();\n\t}\n\n\t/**\n\t * close all tabs that have close icons.\n\t */\n\tpublic void closeClosableTabs() {\n\t\tfor (int i = this.getTabCount() - 1; i >= 0; i--) {\n\t\t\tIcon icon = getIconAt(i);\n\t\t\tif (icon == null)\n\t\t\t\treturn;\n\n\t\t\tthis.closeTabCommon(i);\n\t\t}\n\t}\n\n\t/**\n\t * close the currently selected tab.\n\t */\n\tpublic void closeSelectedTab() {\n\t\tint i = this.getSelectedIndex();\n\t\tthis.closeTabCommon(i);\n\t}\n\n\t/**\n\t * common internal method that checks if a tab can be closed before closing\n\t * it\n\t * \n\t * @param i\n\t *            the tab index\n\t */\n\tprivate void closeTabCommon(int i) {\n\t\tComponent c = this.getComponentAt(i);\n\t\tif (c instanceof DockableView) {\n\t\t\t((DockableView) c).close();\n\t\t} else\n\t\t\tthis.removeTabAt(i);\n\t}\n\n\t/**\n\t * handle mouse click on a tab icon.\n\t * \n\t * @param e\n\t *            the e\n\t */\n\t@Override\n\tpublic void mouseClicked(MouseEvent e) {\n\t\t// check is mouse is on a tab\n\t\tint tabNumber = getUI().tabForCoordinate(this, e.getX(), e.getY());\n\t\tif (tabNumber < 0)\n\t\t\treturn;\n\n\t\t// get icons\n\t\tCloseTabIcon icon = (CloseTabIcon) getIconAt(tabNumber);\n\t\tif (icon == null)\n\t\t\treturn;\n\n\t\t// perform action of the click was on an icon\n\t\tif (icon.isMouseOnDelete(e.getX(), e.getY()))\n\t\t\tthis.closeTabCommon(tabNumber);\n\t\telse if (icon.isMouseOnUndock(e.getX(), e.getY()))\n\t\t\tthis.undock();\n\n\t\tlastMouseEvent = e;\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent\n\t * )\n\t */\n\t@Override\n\tpublic void mouseDragged(MouseEvent arg0) {\n\t\t// empty\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseEntered(MouseEvent e) {\n\t\tmouseMoved(e);\n\t\tlastMouseEvent = e;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseExited(MouseEvent e) {\n\t\tmouseMoved(e);\n\t\tlastMouseEvent = e;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseMoved(MouseEvent e) {\n\n\t\t// if the mouse is on a tab - then highlight it\n\t\tfor (int tabNumber = 0; tabNumber < this.getComponentCount(); tabNumber++) {\n\t\t\tCloseTabIcon icon = (CloseTabIcon) getIconAt(tabNumber);\n\t\t\tif (icon == null)\n\t\t\t\treturn;\n\n\t\t\ticon.paintHighlight(e);\n\t\t}\n\t\tlastMouseEvent = e;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mousePressed(MouseEvent e) {\n\t\tlastMouseEvent = e;\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see\n\t * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)\n\t */\n\t@Override\n\tpublic void mouseReleased(MouseEvent e) {\n\t\tlastMouseEvent = e;\n\t}\n\n\t/**\n\t * undock the currently selected tab.\n\t */\n\tpublic void undock() {\n\t\tComponent c = getSelectedComponent();\n\t\tif (c != null && c instanceof DockableView) {\n\t\t\tDockableView dv = (DockableView) c;\n\t\t\tdv.openInFrame();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/LimitDocument.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.text.AttributeSet;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.PlainDocument;\nimport java.awt.*;\n\n/**\n * document that limits length\n */\npublic class LimitDocument extends PlainDocument {\n\tprivate static final long serialVersionUID = 1L;\n\tint maxLength; // max characters allowed\n\n\t/**\n\t * constructor\n\t * @param maxLen max characters allowed\n\t */\n\tpublic LimitDocument(int max) {\n\t\tthis.maxLength = max;\n\t}\n\n\t@Override\n\tpublic void insertString(int offs, String str, AttributeSet attr)\n\t\t\tthrows BadLocationException {\n\t\tif (getLength() + str.length() > this.maxLength) {\n\t\t\tToolkit.getDefaultToolkit().beep();\n\t\t} else {\n\t\t\tsuper.insertString(offs, str, attr);\n\t\t}\n\t}\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/MyDateChooser.java",
    "content": "package net.sf.borg.ui.util;\n\nimport com.toedter.calendar.IDateEditor;\nimport com.toedter.calendar.JDateChooser;\n\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.io.Serial;\nimport java.util.Calendar;\nimport java.util.Date;\n\npublic class MyDateChooser extends JDateChooser {\n\n    @Serial\n    private static final long serialVersionUID = 120268996535847635L;\n\n    public MyDateChooser(IDateEditor var1) {\n        super(var1);\n        this.calendarButton.addActionListener(this);\n        calendarButton.setMnemonic(KeyEvent.VK_F24); // set to harmless key\n\n    }\n\n    public MyDateChooser()\n    {\n        super(new PlainDateEditor());\n        this.calendarButton.addActionListener(this);\n        calendarButton.setMnemonic(KeyEvent.VK_F24); // set to harmless key\n    }\n\n    @Override\n    public void actionPerformed(ActionEvent actionEvent) {\n        int x = this.calendarButton.getWidth() - (int)this.popup.getPreferredSize().getWidth();\n        int y = this.calendarButton.getY() + this.calendarButton.getHeight();\n        Calendar var4 = Calendar.getInstance();\n        Date var5 = this.dateEditor.getDate();\n        if (var5 != null) {\n            var4.setTime(var5);\n        }\n\n        this.jcalendar.setCalendar(var4);\n\n        // adjust y if no room due to windows taskbar. Java adjusts based\n        // on screen bottom, but ignores the taskbar\n\n        int popUpHeight = popup.getPreferredSize().height;\n        //determines the max available size of the screen\n        Rectangle windowBounds = GraphicsEnvironment.\n                getLocalGraphicsEnvironment().getMaximumWindowBounds();\n        Point invokerPosition = this.calendarButton.getLocationOnScreen();\n\n        if (invokerPosition.y + popUpHeight > windowBounds.height) {\n            //get the negative margin and use it as the y-offset\n            y = windowBounds.height - (invokerPosition.y + popUpHeight);\n        }\n\n        this.popup.show(this.calendarButton, x, y);\n        this.dateSelected = false;\n    }\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/PasswordHelper.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2009 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\nimport java.util.Date;\nimport java.util.logging.Logger;\n\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPasswordField;\n\nimport net.sf.borg.common.EncryptionHelper;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\n\n/**\n * PasswordHelper provides logic to prompt for an encryption password and cache\n * the password for a given amount of time\n */\npublic class PasswordHelper {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\t/* the singleton */\n\tprivate static PasswordHelper singleton = null;\n\n\t/* the cached password */\n\tprivate String keyStorePassword = null;\n\n\t/* the creation date of the password */\n\tprivate Date creationDate = new Date();\n\n\t/**\n\t * get a reference to the singleton PasswordHelper\n\t * \n\t * @return the PasswordHelper singleton\n\t */\n\tpublic static PasswordHelper getReference() {\n\t\tif (singleton == null)\n\t\t\tsingleton = new PasswordHelper();\n\t\treturn singleton;\n\t}\n\n\t/**\n\t * returns the cached password or prompts the user to enter one if none exists\n\t * or the current one is expired\n\t * \n\t * @return the password\n\t * @throws Exception\n\t */\n\tpublic String getEncryptionKeyWithTimeout(String reason) throws Exception {\n\t\t// always check current value of password expiration time in prefs in case it\n\t\t// has changed\n\t\tint pw_ttl = Prefs.getIntPref(PrefName.PASSWORD_TTL);\n\t\tDate expirationDate = new Date();\n\t\texpirationDate.setTime(creationDate.getTime() + 1000 * pw_ttl);\n\t\tif (keyStorePassword == null || expirationDate.before(new Date())) {\n\t\t\tpromptForKeyStorePassword(reason);\n\t\t}\n\n\t\treturn keyStorePassword;\n\t}\n\n\tpublic String getEncryptionKeyWithoutTimeout(String reason) throws Exception {\n\n\t\tif (keyStorePassword == null) {\n\t\t\tpromptForKeyStorePassword(reason);\n\t\t}\n\n\t\treturn keyStorePassword;\n\t}\n\n\tpublic String decryptText(String text, String reason, boolean timeout) throws Exception {\n\t\tString kpw = null;\n\t\tif (timeout)\n\t\t\tkpw = PasswordHelper.getReference().getEncryptionKeyWithTimeout(reason);\n\t\telse\n\t\t\tkpw = PasswordHelper.getReference().getEncryptionKeyWithoutTimeout(reason);\n\n\t\tif (kpw == null) {\n\t\t\tlog.info(\"Cannot unlock encrytion key for: \" + reason);\n\t\t\treturn null;\n\t\t}\n\n\t\tEncryptionHelper helper = new EncryptionHelper(kpw);\n\n\t\treturn (helper.decrypt(text));\n\n\t}\n\n\tprivate void promptForKeyStorePassword(String reason) throws Exception {\n\n\t\twhile (true) {\n\t\t\t// prompt for a new password\n\t\t\tJLabel label = new JLabel(Resource.getResourceString(\"Password_Prompt\") + \" \" + reason);\n\t\t\tJPasswordField jpf = new JPasswordField();\n\t\t\tint result = JOptionPane.showConfirmDialog(null, new Object[] { label, jpf },\n\t\t\t\t\tResource.getResourceString(\"Password\"), JOptionPane.OK_CANCEL_OPTION);\n\t\t\tif (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {\n\t\t\t\tkeyStorePassword = null;\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tkeyStorePassword = new String(jpf.getPassword());\n\n\t\t\t\t// validate\n\t\t\t\ttry {\n\t\t\t\t\tnew EncryptionHelper(keyStorePassword);\n\n\t\t\t\t\t// set expiration\n\t\t\t\t\tcreationDate = new Date();\n\t\t\t\t\treturn;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tkeyStorePassword = null;\n\t\t\t\t\tErrmsg.getErrorHandler().errmsg(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/PlainDateEditor.java",
    "content": "package net.sf.borg.ui.util;\n\nimport com.toedter.calendar.IDateEditor;\nimport com.toedter.calendar.JTextFieldDateEditor;\n\nimport javax.swing.*;\nimport javax.swing.event.CaretEvent;\nimport java.util.Date;\n\npublic class PlainDateEditor extends JTextFieldDateEditor implements IDateEditor {\n\n\t\n\tprivate static final long serialVersionUID = -209404652507744986L;\n\n\tprotected void setDate(Date date, boolean firePropertyChange) {\n\t\t\n\t\tsuper.setDate(date, firePropertyChange);\n\t\tsetForeground(new JLabel().getForeground());\n\t\t\n\t}\n\t\n\tpublic void caretUpdate(CaretEvent event) {\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/PopupMenuHelper.java",
    "content": "/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.util;\n\nimport net.sf.borg.common.Resource;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.*;\n\n/**\n * Helps create and manage context menus for a JTable.\n * \n * @author membar\n */\npublic class PopupMenuHelper {\n\n\t/**\n\t * Holds the Listener and ResourceKey for a Popup Menu Item\n\t */\n\tpublic static class Entry {\n\n\t\t/** The listener. */\n\t\tprivate final ActionListener listener;\n\n\t\t/** The resource key. */\n\t\tprivate final String resourceKey;\n\n\t\t/**\n\t\t * Instantiates a new entry.\n\t\t * \n\t\t * @param listener\n\t\t *            the listener\n\t\t * @param resourceKey\n\t\t *            the resource key\n\t\t */\n\t\tpublic Entry(ActionListener listener, String resourceKey) {\n\t\t\tthis.listener = listener;\n\t\t\tthis.resourceKey = resourceKey;\n\t\t}\n\n\t\t/**\n\t\t * Gets the listener.\n\t\t * \n\t\t * @return the listener\n\t\t */\n\t\tpublic final ActionListener getListener() {\n\t\t\treturn listener;\n\t\t}\n\n\t\t/**\n\t\t * Gets the resource key.\n\t\t * \n\t\t * @return the resource key\n\t\t */\n\t\tpublic final String getResourceKey() {\n\t\t\treturn resourceKey;\n\t\t}\n\t}\n\n\t/**\n\t * mouse adapter for popping up the popup menu on right click for the\n\t * selected row\n\t */\n\tprivate class MyPopupListener extends MouseAdapter {\n\n\t\t/**\n\t\t * show the popup menu if needed\n\t\t * \n\t\t * @param e\n\t\t *            the mouse event\n\t\t */\n\t\tprivate void maybeShowPopup(MouseEvent e) {\n\t\t\tif (e.isPopupTrigger()) {\n\t\t\t\t// If the row we're right-clicking on isn't selected, select\n\t\t\t\t// only that row.\n\t\t\t\tif (component instanceof JTable) {\n\t\t\t\t\tJTable t = (JTable) component;\n\t\t\t\t\tint row = t.rowAtPoint(e.getPoint());\n\t\t\t\t\tif (row != -1 && !t.isRowSelected(row)) {\n\t\t\t\t\t\tt.getSelectionModel().setSelectionInterval(row, row);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (component instanceof JList) {\n\t\t\t\t\tJList<?> t = (JList<?>) component;\n\t\t\t\t\tint index = t.locationToIndex(e.getPoint());\n\t\t\t\t\tif (index != -1 && !t.isSelectedIndex(index)) {\n\t\t\t\t\t\tt.setSelectedIndex(index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpopup.show(e.getComponent(), e.getX(), e.getY());\n\t\t\t}\n\t\t}\n\n\t\t// MouseAdapter overrides\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see\n\t\t * java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)\n\t\t */\n\t\t@Override\n\t\tpublic void mousePressed(MouseEvent e) {\n\t\t\tmaybeShowPopup(e);\n\t\t}\n\n\t\t/*\n\t\t * (non-Javadoc)\n\t\t * \n\t\t * @see\n\t\t * java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)\n\t\t */\n\t\t@Override\n\t\tpublic void mouseReleased(MouseEvent e) {\n\t\t\tmaybeShowPopup(e);\n\t\t}\n\t}\n\n\n\t/** The popup menu. */\n\tprivate final JPopupMenu popup;\n\n\t/** The table. */\n\tprivate final JComponent component;\n\n\tstatic public JPopupMenu createPopupMenu(Entry[] entries) {\n\t\t\n\t\tJPopupMenu p = new JPopupMenu();\n\n\t\t// create menu items from the entries\n\t\tfor (int i = 0; i < entries.length; ++i) {\n\t\t\tEntry entry = entries[i];\n\t\t\tJMenuItem mnuitm = new JMenuItem();\n\t\t\tp.add(mnuitm);\n\t\t\tmnuitm.setText(Resource.getResourceString(entry.getResourceKey()));\n\t\t\tmnuitm.addActionListener(entry.getListener());\n\t\t}\n\t\t\n\t\treturn p;\n\t}\n\n\t/**\n\t * Instantiates a new popup menu helper.\n\t * \n\t * @param table\n\t *            the table\n\t * @param entries\n\t *            the entries\n\t */\n\tpublic PopupMenuHelper(final JComponent c, Entry[] entries) {\n\t\tthis.component = c;\n\n\t\tpopup = createPopupMenu(entries);\n\n\t\t// listen for mouse events\n\t\tc.addMouseListener(new MyPopupListener());\n\n\t\t// listen for context menu key event\n\t\tc.addKeyListener(new KeyAdapter() {\n\t\t\t@Override\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\t\t\t\tswitch (e.getKeyCode()) {\n\t\t\t\tcase 0x020D:\n\t\t\t\t\t// this is the KeyEvent.VK_CONTEXT_MENU under JDK 1.5\n\t\t\t\t\tif (component instanceof JTable) {\n\t\t\t\t\t\tJTable table = (JTable) component;\n\t\t\t\t\t\tint[] selIndices = table.getSelectedRows();\n\t\t\t\t\t\tif (selIndices.length == 0)\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tint rowIndex = selIndices[0];\n\t\t\t\t\t\tRectangle rct = table.getCellRect(rowIndex, 0, false);\n\t\t\t\t\t\tpopup.show(c, rct.x, rct.y + rct.height);\n\t\t\t\t\t} else if (component instanceof JList) {\n\t\t\t\t\t\tJList<?> t = (JList<?>) component;\n\t\t\t\t\t\tint index = t.getSelectedIndex();\n\t\t\t\t\t\tif (index == -1)\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tRectangle rct = t.getCellBounds(index, index);\n\t\t\t\t\t\tpopup.show(c, rct.x, rct.y + rct.height);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/PwMigration.java",
    "content": "package net.sf.borg.ui.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\nimport java.util.Base64;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.CipherOutputStream;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport net.sf.borg.common.EncryptionHelper;\nimport net.sf.borg.common.Errmsg;\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\n\n// one time password migration for old email passwords\npublic class PwMigration {\n\n\tprivate static final PrefName PWMIGRATED = new PrefName(\"pwmigrated\", \"false\");\n\tprivate static final PrefName EMAILPASS2 = new PrefName(\"email_pass2\", \"\");\n\n\tstatic public void migratePasswords() {\n\n\t\tboolean done = Prefs.getBoolPref(PWMIGRATED);\n\t\tif (done)\n\t\t\treturn;\n\n\t\tString keystore = Prefs.getPref(PrefName.KEYSTORE);\n\n\t\ttry {\n\n\t\t\t// if there is a keystore, then migrate the passwords\n\t\t\tif (keystore != null && !keystore.isEmpty()) {\n\n\t\t\t\tString emailKey = Prefs.getPref(EMAILPASS2);\n\t\t\t\tif (emailKey != null && !emailKey.isEmpty()) {\n\t\t\t\t\t// decrypt existing pw\n\t\t\t\t\tString pw = emailgep();\n\n\t\t\t\t\t// encrypt from new key and store\n\t\t\t\t\tEncryptionHelper helper = new EncryptionHelper(\n\t\t\t\t\t\t\tPasswordHelper.getReference().getEncryptionKeyWithoutTimeout(\"Migrate Email Password\"));\n\t\t\t\t\tPrefs.putPref(PrefName.EMAILPASS, helper.encrypt(pw));\n\n\t\t\t\t\t// delete old pref key\n\t\t\t\t\tPrefs.delPref(EMAILPASS2);\n\t\t\t\t}\n\n\t\t\t\t\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\tPrefs.delPref(PrefName.EMAILPASS);\n\t\t\t\tPrefs.delPref(EMAILPASS2);\n\n\t\t\t}\n\n\n\t\t} catch (Exception e) {\n\t\t\tErrmsg.getErrorHandler().notice(\"Password Migration Failed. Please manually reset passwords.\");\n\t\t\tPrefs.delPref(PrefName.EMAILPASS);\n\t\t\tPrefs.delPref(EMAILPASS2);\n\t\t}\n\t\t\n\t\t\n\t\tPrefs.putPref(PWMIGRATED, \"true\");\n\n\n\t}\n\n\t\n\n\t// legacy code for the email password\n\tprivate static String emailgep() throws Exception {\n\t\tString p1 = Prefs.getPref(EMAILPASS2);\n\t\tString p2 = Prefs.getPref(PrefName.EMAILPASS);\n\t\tif (\"\".equals(p2))\n\t\t\treturn p2;\n\n\t\tif (\"\".equals(p1)) {\n\t\t\treturn p2;\n\t\t}\n\n\t\tbyte[] ba = Base64.getDecoder().decode(p1);\n\t\tSecretKey key = new SecretKeySpec(ba, \"AES\");\n\t\tCipher dec = Cipher.getInstance(\"AES\");\n\t\tdec.init(Cipher.DECRYPT_MODE, key);\n\t\tbyte[] decba = Base64.getDecoder().decode(p2);\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tOutputStream os = new CipherOutputStream(baos, dec);\n\t\tos.write(decba);\n\t\tos.close();\n\n\t\treturn baos.toString();\n\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/ScrolledDialog.java",
    "content": "/*\nThis file is part of BORG.\n\n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nCopyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.common.Resource;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.logging.Logger;\n\n/**\n * Common dialog class with a scrolled text area.\n */\n\npublic class ScrolledDialog extends JDialog {\n\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\tprivate static final long serialVersionUID = 1L;\n\tpublic static final int CANCEL = 1;\n\t/** exception - if we are showing one */\n\tprivate static Exception e_;\n\n\t/* return codes for option dialog */\n\tpublic static final int OK = 0;\n\n\t/* result for option dialog */\n\tprivate static int result;\n\n\t/**\n\t * Show an error dialog with optional stack trace button.\n\t * \n\t * @param e\n\t *            the Exception\n\t * \n\t */\n\tpublic static void showError(Exception e) {\n\t\te_ = e;\n\t\tboolean ss = false;\n\t\tString showstack = Prefs.getPref(PrefName.STACKTRACE);\n\t\tif (showstack.equals(\"true\")) {\n\t\t\tss = true;\n\t\t}\n\n\t\tnew ScrolledDialog(e.toString(), ss, false).setVisible(true);\n\t}\n\n\t/**\n\t * Show a notice.\n\t * \n\t * @param text\n\t *            the notice text\n\t * \n\t */\n\tpublic static void showNotice(String text) {\n\t\tnew ScrolledDialog(text, false, false).setVisible(true);\n\t}\n\n\t/**\n\t * show a scrolled option dialog\n\t * \n\t * @param message\n\t *            the message to show\n\t * @return the result, OK or CANCEL\n\t */\n\tpublic static int showOptionDialog(String message) {\n\t\tnew ScrolledDialog(message, false, true).setVisible(true);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Show a table.\n\t * \n\t * @param tbl\n\t *            the table\n\t */\n\tpublic static void showTable(JTable tbl) {\n\t\tnew ScrolledDialog(tbl).setVisible(true);\n\t}\n\n\t/** JTable - if we are showing one */\n\tprivate JTable tbl_ = null;\n\n\t/**\n\t * Instantiates a new scrolled dialog to show a table.\n\t * \n\t * @param tbl\n\t *            the tbl\n\t */\n\tprivate ScrolledDialog(JTable tbl) {\n\t\ttbl_ = tbl;\n\t\tinitComponents(null, false, false);\n\t\tsetModal(false);\n\t}\n\n\t/**\n\t * Instantiates a new scrolled dialog to show text.\n\t * \n\t * @param s\n\t *            the text to show\n\t * @param stack\n\t *            if true, show the stack request button\n\t * @param option\n\t *            if true, show a cancel button and set the result\n\t */\n\tprivate ScrolledDialog(String s, boolean stack, boolean option) {\n\t\tinitComponents(s, option, stack);\n\t\tsetModal(true);\n\t}\n\n\t/**\n\t * Inits the swing components.\n\t */\n\tprivate void initComponents(String text, boolean isOption, boolean isStack) {\n\t\tsetDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);\n\t\tsetTitle(\"BORG\");\n\t\tthis.setSize(165, 300);\n\n\t\tJPanel mainPanel = new JPanel();\n\t\tmainPanel.setLayout(new GridBagLayout());\n\t\t\n\t\tJScrollPane scrollPane = new JScrollPane();\n\t\tscrollPane\n\t\t\t\t.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n\t\tscrollPane.setPreferredSize(new java.awt.Dimension(600, 200));\n\t\t\n\t\t/* set scroll context to message or table */\n\t\tif (tbl_ != null) {\n\t\t\tscrollPane.setViewportView(tbl_);\n\t\t} else {\n\t\t\tJTextArea textArea = new JTextArea();\n\t\t\ttextArea.setEditable(false);\n\t\t\ttextArea.setLineWrap(true);\n\t\t\ttextArea.setText(text);\n\t\t\tscrollPane.setViewportView(textArea);\n\t\t}\n\t\t\n\t\tmainPanel.add(scrollPane, GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0));\n\n\t\t/*\n\t\t * add the buttons\n\t\t */\n\t\tJPanel buttonPanel = new JPanel();\n\n\t\t/* always add ok button */\n\t\tJButton okButton = new JButton();\n\t\tokButton.setText(Resource.getResourceString(\"OK\"));\n\t\tokButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\tresult = OK;\n\t\t\t\tdispose();\n\t\t\t}\n\t\t});\n\t\tbuttonPanel.add(okButton, null);\n\n\t\t/* only add stack button if needed */\n\t\tif (isStack) {\n\t\t\tJButton stackButton = new JButton();\n\t\t\tstackButton.setText(Resource.getResourceString(\"Show_Stack_Trace\"));\n\t\t\tstackButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\t// show the stack trace\n\t\t\t\t\tjava.io.ByteArrayOutputStream bao = new java.io.ByteArrayOutputStream();\n\t\t\t\t\tjava.io.PrintStream ps = new java.io.PrintStream(bao);\n\t\t\t\t\te_.printStackTrace(ps);\n\t\t\t\t\tScrolledDialog.showNotice(bao.toString());\n\t\t\t\t\t// dump to log \n\t\t\t\t\tlog.severe(bao.toString());\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(stackButton, null);\n\t\t}\n\n\t\t/* only add cancel button if this is an option dialog */\n\t\tif (isOption) {\n\t\t\tJButton cancelButton = new JButton();\n\t\t\tcancelButton.setText(Resource.getResourceString(\"Cancel\"));\n\t\t\tcancelButton.addActionListener(new java.awt.event.ActionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void actionPerformed(java.awt.event.ActionEvent e) {\n\t\t\t\t\tresult = CANCEL;\n\t\t\t\t\tdispose();\n\t\t\t\t}\n\t\t\t});\n\t\t\tbuttonPanel.add(cancelButton, null);\n\n\t\t}\n\t\t\n\t\tmainPanel.add(buttonPanel, GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH));\n\t\tthis.setContentPane(mainPanel);\n\t\t\n\t\taddWindowListener(new java.awt.event.WindowAdapter() {\n\t\t\t@Override\n\t\t\tpublic void windowClosing(java.awt.event.WindowEvent evt) {\n\t\t\t\twindowClose();\n\t\t\t}\n\t\t});\n\n\t\tpack();\n\n\t\tDimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n\t\tDimension labelSize = scrollPane.getPreferredSize();\n\t\tsetLocation(screenSize.width / 2 - (labelSize.width / 2),\n\t\t\t\tscreenSize.height / 2 - (labelSize.height / 2));\n\t}\n\n\t/**\n\t * dispose of the window\n\t * \n\t */\n\tprivate void windowClose() {\n\t\tthis.dispose();\n\t}\n\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/SplashScreen.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\n\n/*\n * banner.java\n *\n * Created on January 25, 2003, 8:25 PM\n */\n\npackage net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * Splash Screen with status text\n */\npublic class SplashScreen extends JDialog {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/** The image label to show the splash image */\n\tprivate JLabel imageLabel;\n\n\t/** The status text. */\n\tprivate JTextField statusText;\n\n\t/**\n\t * constructor\n\t */\n\tpublic SplashScreen(ModalityType modal) {\n\t\t\n\t\tsetUndecorated(true);\n\t\tsetModalityType(modal);\n\t\tinitComponents();\n\t\tpack();\n\t\t\n\t\t// size according to image and center on screen\n\t\tDimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n\t\tDimension labelSize = imageLabel.getPreferredSize();\n\t\tsetLocation(screenSize.width / 2 - (labelSize.width / 2),\n\t\t\t\tscreenSize.height / 2 - (labelSize.height / 2));\n\t}\n\n\t/**\n\t * Inits the components.\n\t */\n\tprivate void initComponents() {\n\n\t\timageLabel = new JLabel();\n\t\tstatusText = new JTextField();\n\n\t\tgetContentPane().setLayout(new java.awt.GridBagLayout());\n\n\t\tsetLocationRelativeTo(this);\n\n\t\t// just put an image in imageLabel\n\t\timageLabel.setHorizontalAlignment(SwingConstants.CENTER);\n\t\timageLabel.setIcon(new ImageIcon(getClass().getResource(\n\t\t\t\t\"/resource/borg.jpg\")));\n\t\timageLabel.setIconTextGap(0);\n\t\timageLabel.setOpaque(true);\n\n\t\tstatusText.setColumns(25);\n\t\tstatusText.setMinimumSize(new java.awt.Dimension(40, 19));\n\n\t\tJPanel topPanel = new JPanel();\n\t\ttopPanel.setLayout(new GridBagLayout());\n\t\t\n\t\tGridBagConstraints gbc1 = GridBagConstraintsFactory.create(0, 0,\n\t\t\t\tGridBagConstraints.BOTH, 1.0, 1.0);\n\t\tgbc1.insets = new Insets(0,0,0,0);\n\t\ttopPanel.add(imageLabel,gbc1);\n\t\t\n\t\tGridBagConstraints gbc2 = GridBagConstraintsFactory.create(0, 1,\n\t\t\t\tGridBagConstraints.BOTH, 0.0, 0.0);\n\t\tgbc2.insets = new Insets(0,0,0,0);\n\t\ttopPanel.add(statusText, gbc2);\n\t\t\n\t\tstatusText.setEditable(false);\n\n\t\tthis.setSize(364, 322);\n\n\t\tthis.setContentPane(topPanel);\n\n\t\tpack();\n\t}\n\n\t/**\n\t * Sets the text.\n\t * \n\t * @param tx the new text\n\t */\n\tpublic void setText(String tx) {\n\t\tstatusText.setText(tx);\n\t}\n} \n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/TablePrinter.java",
    "content": "/* this code was loosely based on code obtained from an online forum\n * that did not contain any copyright information */\n\n/*\n This file is part of BORG.\n\n BORG is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n BORG is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with BORG; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n Copyright 2003 by Mike Berger\n */\npackage net.sf.borg.ui.util;\n\nimport net.sf.borg.common.PrintHelper;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.print.PageFormat;\nimport java.awt.print.Printable;\nimport java.awt.print.PrinterException;\n\n/**\n * wraps a JTable in a class to make it Printable. contains a methos to send the JTable to a printer\n */\npublic class TablePrinter implements Printable {\n\n\t/**\n\t * Sends a JTable to a printer\n\t * \n\t * @param tbl the table\n\t * \n\t * @throws Exception \n\t */\n\tstatic public void printTable(JTable tbl) throws Exception {\n\t\t// use print helper to print the table\n\t\tPrintHelper.printPrintable(new TablePrinter(tbl));\n\t}\n\n\t/** The table */\n\tprivate final JTable theTable;\n\n\t/**\n\t * constructor\n\t * \n\t * @param c the table\n\t */\n\tprivate TablePrinter(JTable c) {\n\t\ttheTable = c;\n\t}\n\n\t/* (non-Javadoc)\n\t * @see java.awt.print.Printable#print(java.awt.Graphics, java.awt.print.PageFormat, int)\n\t */\n\t@Override\n\tpublic int print(Graphics g, PageFormat pageFormat, int pageIndex)\n\t\t\tthrows PrinterException {\n\t\t\n\t\tGraphics2D g2 = (Graphics2D) g;\n\t\t\n\t\tg2.setColor(Color.black);\n\t\t\n\t\tint fontHeight = g2.getFontMetrics().getHeight();\n\t\tint fontDesent = g2.getFontMetrics().getDescent();\n\n\t\t// leave room for page number\n\t\tdouble pageHeight = pageFormat.getImageableHeight() - fontHeight;\n\t\t\n\t\tdouble pageWidth = pageFormat.getImageableWidth();\n\t\t\n\t\tdouble tableWidth = theTable.getColumnModel().getTotalColumnWidth();\n\t\t\n\t\t// set the scaling if the table is wider than the page\n\t\tdouble scale = 1.0;\n\t\tif (tableWidth >= pageWidth) {\n\t\t\tscale = pageWidth / tableWidth;\n\t\t}\n\n\t\tdouble headerHeightOnPage = theTable.getTableHeader().getHeight()\n\t\t\t\t* scale;\n\t\t\n\t\tdouble tableWidthOnPage = tableWidth * scale;\n\n\t\tdouble oneRowHeight = (theTable.getRowHeight() + theTable\n\t\t\t\t.getRowMargin())\n\t\t\t\t* scale;\n\t\t\n\t\tint numRowsOnAPage = (int) ((pageHeight - headerHeightOnPage) / oneRowHeight);\n\t\t\n\t\tdouble pageHeightForTable = oneRowHeight * numRowsOnAPage;\n\t\t\n\t\tint totalNumPages = (int) Math.ceil(((double) theTable.getRowCount())\n\t\t\t\t/ numRowsOnAPage);\n\t\t\n\t\t// if we are being called to print a page that is more than the number\n\t\t// needed to print the table - then end the printout\n\t\tif (pageIndex >= totalNumPages) {\n\t\t\treturn NO_SUCH_PAGE;\n\t\t}\n\n\t\tg2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());\n\t\t\n\t\t// draw page number at bottom center\n\t\tg2.drawString(\"Page: \" + (pageIndex + 1), (int) pageWidth / 2 - 35,\n\t\t\t\t(int) (pageHeight + fontHeight - fontDesent));\n\n\t\tg2.translate(0f, headerHeightOnPage);\n\t\tg2.translate(0f, -pageIndex * pageHeightForTable);\n\n\t\t// If this piece of the table is smaller\n\t\t// than the size available because we are on the last page, then\n\t\t// clip to the appropriate bounds.\n\t\tif (pageIndex + 1 == totalNumPages) {\n\t\t\tint lastRowPrinted = numRowsOnAPage * pageIndex;\n\t\t\tint numRowsLeft = theTable.getRowCount() - lastRowPrinted;\n\t\t\tg2.setClip(0, (int) (pageHeightForTable * pageIndex), (int) Math\n\t\t\t\t\t.ceil(tableWidthOnPage), (int) Math.ceil(oneRowHeight\n\t\t\t\t\t* numRowsLeft));\n\t\t}\n\t\t// else clip to the entire area available.\n\t\telse {\n\t\t\tg2.setClip(0, (int) (pageHeightForTable * pageIndex), (int) Math\n\t\t\t\t\t.ceil(tableWidthOnPage), (int) Math\n\t\t\t\t\t.ceil(pageHeightForTable));\n\t\t}\n\n\t\t// scale as needed to fit width\n\t\tg2.scale(scale, scale);\n\t\t\n\t\t// paint the table onto the graphics\n\t\ttheTable.paint(g2);\n\t\t\n\t\t// reverse the scaling\n\t\tg2.scale(1 / scale, 1 / scale);\n\t\t\n\t\t// paint the table header at the top\n\t\tg2.translate(0f, pageIndex * pageHeightForTable);\n\t\tg2.translate(0f, -headerHeightOnPage);\n\t\tg2.setClip(0, 0, (int) Math.ceil(tableWidthOnPage), (int) Math\n\t\t\t\t.ceil(headerHeightOnPage));\n\t\tg2.scale(scale, scale);\n\t\ttheTable.getTableHeader().paint(g2);\n\n\t\treturn Printable.PAGE_EXISTS;\n\t}\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/TableSorter.java",
    "content": "package net.sf.borg.ui.util;\n\nimport javax.swing.*;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.event.TableModelListener;\nimport javax.swing.table.*;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.List;\nimport java.util.*;\n\n/**\n * TableSorter is a decorator for TableModels; adding sorting\n * functionality to a supplied TableModel. TableSorter does\n * not store or copy the data in its TableModel; instead it maintains\n * a map from the row indexes of the view to the row indexes of the\n * model. As requests are made of the sorter (like getValueAt(row, col))\n * they are passed to the underlying model after the row numbers\n * have been translated via the internal mapping array. This way,\n * the TableSorter appears to hold another copy of the table\n * with the rows in a different order.\n * <p/>\n * TableSorter registers itself as a listener to the underlying model,\n * just as the JTable itself would. Events recieved from the model\n * are examined, sometimes manipulated (typically widened), and then\n * passed on to the TableSorter's listeners (typically the JTable).\n * If a change to the model has invalidated the order of TableSorter's\n * rows, a note of this is made and the sorter will resort the\n * rows the next time a value is requested.\n * <p/>\n * When the tableHeader property is set, either by using the\n * setTableHeader() method or the two argument constructor, the\n * table header may be used as a complete UI for TableSorter.\n * The default renderer of the tableHeader is decorated with a renderer\n * that indicates the sorting status of each column. In addition,\n * a mouse listener is installed with the following behavior:\n * <ul>\n * <li>\n * Mouse-click: Clears the sorting status of all other columns\n * and advances the sorting status of that column through three\n * values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to\n * NOT_SORTED again).\n * <li>\n * SHIFT-mouse-click: Clears the sorting status of all other columns\n * and cycles the sorting status of the column through the same\n * three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.\n * <li>\n * CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except\n * that the changes to the column do not cancel the statuses of columns\n * that are already sorting - giving a way to initiate a compound\n * sort.\n * </ul>\n * <p/>\n * This is a long overdue rewrite of a class of the same name that\n * first appeared in the swing table demos in 1997.\n * \n * @author Philip Milne\n * @author Brendon McLean \n * @author Dan van Enckevort\n * @author Parwinder Sekhon\n * @version 2.0 02/27/04\n */\n\n/* updated by Mike Berger \n * \n * - lots didn't work out of the box. Plus code was added to make this object\n *   compatible with the old TableSorter so the rest of the code did not have to change\n */\n\n@SuppressWarnings(\"unchecked\") //$NON-NLS-1$\npublic class TableSorter extends AbstractTableModel {\n    private static final long serialVersionUID = 1L;\n\n    protected TableModel tableModel;\n\n    public static final int DESCENDING = -1;\n    public static final int NOT_SORTED = 0;\n    public static final int ASCENDING = 1;\n\n    private static final Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);\n\n    public static final Comparator<Object> COMPARABLE_COMAPRATOR = new Comparator<Object>() {\n        @Override\n        public int compare(Object o1, Object o2) {\n            return ((Comparable<Object>) o1).compareTo(o2);\n        }\n    };\n    public static final Comparator<Object> LEXICAL_COMPARATOR = new Comparator<Object>() {\n        @Override\n        public int compare(Object o1, Object o2) {\n            return o1.toString().compareTo(o2.toString());\n        }\n    };\n    \n    \n\n    private Row[] viewToModel;\n    private int[] modelToView;\n\n    private JTableHeader tableHeader;\n\n\n    private final Map<Class<?>, Comparator<Object>> columnComparators = new HashMap<Class<?>, Comparator<Object>>();\n    private final List<Directive> sortingColumns = new ArrayList<Directive>();\n\n    public TableSorter() {\n      // empty\n    }\n\n    private void clearSortingState() {\n        viewToModel = null;\n        modelToView = null;\n    }\n\n    public TableModel getTableModel() {\n        return tableModel;\n    }\n\n    public void setTableModel(TableModel tableModel) {\n        if (this.tableModel != null) {\n            this.tableModel.removeTableModelListener(tableModelListener);\n        }\n\n        this.tableModel = tableModel;\n        if (this.tableModel != null) {\n            this.tableModel.addTableModelListener(tableModelListener);\n        }\n\n        clearSortingState();\n        fireTableStructureChanged();\n    }\n\n    public JTableHeader getTableHeader() {\n        return tableHeader;\n    }\n\n    public void setTableHeader(JTableHeader tableHeader) {\n        if (this.tableHeader != null) {\n            this.tableHeader.removeMouseListener(mouseListener_);\n            TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();\n            if (defaultRenderer instanceof SortableHeaderRenderer) {\n                this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);\n            }\n        }\n        this.tableHeader = tableHeader;\n        if (this.tableHeader != null) {\n            this.tableHeader.addMouseListener(mouseListener_);\n            this.tableHeader.setDefaultRenderer(\n                    new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));\n        }\n    }\n\n    public boolean isSorting() {\n        return sortingColumns.size() != 0;\n    }\n\n    private Directive getDirective(int column) {\n        for (int i = 0; i < sortingColumns.size(); i++) {\n            Directive directive = sortingColumns.get(i);\n            if (directive.column == column) {\n                return directive;\n            }\n        }\n        return EMPTY_DIRECTIVE;\n    }\n\n    public int getSortingStatus(int column) {\n        return getDirective(column).direction;\n    }\n\n    private void sortingStatusChanged() {\n        clearSortingState();\n        fireTableDataChanged();\n        if (tableHeader != null) {\n            tableHeader.repaint();\n        }\n    }\n\n    public void setSortingStatus(int column, int status) {\n        Directive directive = getDirective(column);\n        if (directive != EMPTY_DIRECTIVE) {\n            sortingColumns.remove(directive);\n        }\n        if (status != NOT_SORTED) {\n            sortingColumns.add(new Directive(column, status));\n        }\n        sortingStatusChanged();\n    }\n\n    protected Icon getHeaderRendererIcon(int column, int size) {\n        Directive directive = getDirective(column);\n        if (directive == EMPTY_DIRECTIVE) {\n            return null;\n        }\n        return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));\n    }\n\n    private void cancelSorting() {\n        sortingColumns.clear();\n        sortingStatusChanged();\n    }\n\n    protected Comparator<Object> getComparator(int column) {\n        Class<?> columnType = tableModel.getColumnClass(column);\n        Comparator<Object> comparator = columnComparators.get(columnType);\n        if (comparator != null) {\n            return comparator;\n        }\n        if (Comparable.class.isAssignableFrom(columnType)) {\n            return COMPARABLE_COMAPRATOR;\n        }\n        return LEXICAL_COMPARATOR;\n    }\n\n    private Row[] getViewToModel() {\n        if (viewToModel == null) {\n            int tableModelRowCount = tableModel.getRowCount();\n            viewToModel = new Row[tableModelRowCount];\n            for (int row = 0; row < tableModelRowCount; row++) {\n                viewToModel[row] = new Row(row);\n            }\n\n            if (isSorting()) {\n                Arrays.sort(viewToModel);\n            }\n        }\n        return viewToModel;\n    }\n\n    public int modelIndex(int viewIndex) {\n        return getViewToModel()[viewIndex].modelIndex;\n    }\n\n    private int[] getModelToView() {\n        if (modelToView == null) {\n            int n = getViewToModel().length;\n            modelToView = new int[n];\n            for (int i = 0; i < n; i++) {\n                modelToView[modelIndex(i)] = i;\n            }\n        }\n        return modelToView;\n    }\n\n    // TableModel interface methods \n\n    @Override\n    public int getRowCount() {\n        return (tableModel == null) ? 0 : tableModel.getRowCount();\n    }\n\n    @Override\n    public int getColumnCount() {\n        return (tableModel == null) ? 0 : tableModel.getColumnCount();\n    }\n\n    @Override\n    public String getColumnName(int column) {\n        return tableModel.getColumnName(column);\n    }\n\n    @Override\n    public Class<?> getColumnClass(int column) {\n        return tableModel.getColumnClass(column);\n    }\n\n    @Override\n    public boolean isCellEditable(int row, int column) {\n        return tableModel.isCellEditable(modelIndex(row), column);\n    }\n\n    @Override\n    public Object getValueAt(int row, int column) {\n        return tableModel.getValueAt(modelIndex(row), column);\n    }\n\n    @Override\n    public void setValueAt(Object aValue, int row, int column) {\n        tableModel.setValueAt(aValue, modelIndex(row), column);\n    }\n\n    // Helper classes\n    \n    private class Row implements Comparable<Object> {\n        private final int modelIndex;\n\n        public Row(int index) {\n            this.modelIndex = index;\n        }\n\n        @Override\n        public int compareTo(Object o) {\n            int row1 = modelIndex;\n            int row2 = ((Row) o).modelIndex;\n\n            for (Iterator<Directive> it = sortingColumns.iterator(); it.hasNext();) {\n                Directive directive = it.next();\n                int column = directive.column;\n                Object o1 = tableModel.getValueAt(row1, column);\n                Object o2 = tableModel.getValueAt(row2, column);\n\n                int comparison = 0;\n                // Define null less than everything, except null.\n                if (o1 == null && o2 == null) {\n                    comparison = 0;\n                } else if (o1 == null) {\n                    comparison = -1;\n                } else if (o2 == null) {\n                    comparison = 1;\n                } else {\n                    comparison = getComparator(column).compare(o1, o2);\n                }\n                if (comparison != 0) {\n                    return directive.direction == DESCENDING ? -comparison : comparison;\n                }\n            }\n            return 0;\n        }\n    }\n\n    private final TableModelListener tableModelListener = new TableModelListener (){\n        @Override\n        public void tableChanged(TableModelEvent e) {\n            // If we're not sorting by anything, just pass the event along.             \n            if (!isSorting()) {\n                clearSortingState();\n                fireTableChanged(e);\n                return;\n            }\n                \n            // If the table structure has changed, cancel the sorting; the             \n            // sorting columns may have been either moved or deleted from             \n            // the model. \n            if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {\n                cancelSorting();\n                fireTableChanged(e);\n                return;\n            }\n\n            // We can map a cell event through to the view without widening             \n            // when the following conditions apply: \n            // \n            // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and, \n            // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and,\n            // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and, \n            // d) a reverse lookup will not trigger a sort (modelToView != null)\n            //\n            // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS.\n            // \n            // The last check, for (modelToView != null) is to see if modelToView \n            // is already allocated. If we don't do this check; sorting can become \n            // a performance bottleneck for applications where cells  \n            // change rapidly in different parts of the table. If cells \n            // change alternately in the sorting column and then outside of             \n            // it this class can end up re-sorting on alternate cell updates - \n            // which can be a performance problem for large tables. The last \n            // clause avoids this problem. \n            int column = e.getColumn();\n            if (e.getFirstRow() == e.getLastRow()\n                    && column != TableModelEvent.ALL_COLUMNS\n                    && getSortingStatus(column) == NOT_SORTED\n                    && modelToView != null) {\n                int viewIndex = getModelToView()[e.getFirstRow()];\n                fireTableChanged(new TableModelEvent(TableSorter.this, \n                                                     viewIndex, viewIndex, \n                                                     column, e.getType()));\n                return;\n            }\n\n            // Something has happened to the data that may have invalidated the row order. \n            clearSortingState();\n            fireTableChanged(e); // MBB\n            fireTableDataChanged();\n            return;\n        }\n    };\n\n    private final MouseAdapter mouseListener_ = new MouseAdapter (){\n        @Override\n        public void mouseClicked(MouseEvent e) {\n            JTableHeader h = (JTableHeader) e.getSource();\n            TableColumnModel columnModel = h.getColumnModel();\n            int viewColumn = columnModel.getColumnIndexAtX(e.getX());\n            int column = columnModel.getColumn(viewColumn).getModelIndex();\n            if (column != -1) {\n                int status = getSortingStatus(column);\n                if (!e.isControlDown()) {\n                    cancelSorting();\n                }\n                // Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or \n                // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed. \n                status = status + (e.isShiftDown() ? -1 : 1);\n                status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, 1}\n                setSortingStatus(column, status);\n            }\n        }\n    };\n    \n    private static class Arrow implements Icon {\n        private final boolean descending;\n        private final int size;\n        private final int priority;\n\n        public Arrow(boolean descending, int size, int priority) {\n            this.descending = descending;\n            this.size = size;\n            this.priority = priority;\n        }\n\n        @Override\n        public void paintIcon(Component c, Graphics g, int x, int yIn) {\n            Color color = c == null ? Color.GRAY : c.getBackground();             \n            // In a compound sort, make each succesive triangle 20% \n            // smaller than the previous one. \n            int dx = (int)(size/2*Math.pow(0.8, priority));\n            int dy = descending ? dx : -dx;\n            // Align icon (roughly) with font baseline. \n            int y = yIn +  5*size/6 + (descending ? -dy : 0);\n            int shift = descending ? 1 : -1;\n            g.translate(x, y);\n\n            // Right diagonal. \n            g.setColor(color.darker());\n            g.drawLine(dx / 2, dy, 0, 0);\n            g.drawLine(dx / 2, dy + shift, 0, shift);\n            \n            // Left diagonal. \n            g.setColor(color.brighter());\n            g.drawLine(dx / 2, dy, dx, 0);\n            g.drawLine(dx / 2, dy + shift, dx, shift);\n            \n            // Horizontal line. \n            if (descending) {\n                g.setColor(color.darker().darker());\n            } else {\n                g.setColor(color.brighter().brighter());\n            }\n            g.drawLine(dx, 0, 0, 0);\n\n            g.setColor(color);\n            g.translate(-x, -y);\n        }\n\n        @Override\n        public int getIconWidth() {\n            return size;\n        }\n\n        @Override\n        public int getIconHeight() {\n            return size;\n        }\n    }\n\n    private class SortableHeaderRenderer implements TableCellRenderer {\n        private final TableCellRenderer tableCellRenderer;\n\n        public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) {\n            this.tableCellRenderer = tableCellRenderer;\n        }\n\n        @Override\n        public Component getTableCellRendererComponent(JTable table, \n                                                       Object value,\n                                                       boolean isSelected, \n                                                       boolean hasFocus,\n                                                       int row, \n                                                       int column) {\n            Component c = tableCellRenderer.getTableCellRendererComponent(table, \n                    value, isSelected, hasFocus, row, column);\n            if (c instanceof JLabel) {\n                JLabel l = (JLabel) c;\n                l.setHorizontalTextPosition(SwingConstants.LEFT);\n                int modelColumn = table.convertColumnIndexToModel(column);\n                l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));\n            }\n            return c;\n        }\n    }\n\n    private static class Directive {\n        private final int column;\n        private final int direction;\n\n        public Directive(int column, int direction) {\n            this.column = column;\n            this.direction = direction;\n        }\n    }\n    \n    \n    \n    // ===============================================================================\n    // compatibility with the old SUN TableSorter\n    // ===============================================================================\n    \n    static private class NewTableModel extends DefaultTableModel{\n        private static final long serialVersionUID = 1L;\n        Class<?> [] classes_;\n        boolean [] editable_;\n        \n        public NewTableModel(String[] cols, Class<?>[] classes, boolean[] editable)\n        {\n            super( cols, 0 );\n            classes_ = classes;\n            if( editable != null )\n        \teditable_ = editable;\n            else\n            {\n        \teditable_ = new boolean[cols.length];\n        \tfor(int i = 0; i < cols.length; i++)\n        \t    editable_[i] = false;\n            }\n        }\n \n        @Override\n        public Class<?> getColumnClass(int column) {\n            return classes_[column];\n        }\n        \n        @Override\n        public boolean isCellEditable(int rowIndex, int columnIndex)\n        {\n            \n            return(editable_[columnIndex]);\n        }\n\n    }\n    \n    public TableSorter(String[] cols, Class<?>[] classes, boolean[] editable)\n    {\n        setTableModel(new NewTableModel(cols,classes, editable));\n    }\n    \n    public TableSorter(String[] cols, Class<?>[] classes)\n    {\n        setTableModel(new NewTableModel(cols,classes, null));\n    }\n\n    /**\n     * @param table1\n     */\n    public void addMouseListenerToHeaderInTable(JTable table1) {\n        table1.setColumnSelectionAllowed(false);\n        setTableHeader(table1.getTableHeader());       \n    }\n\n    /**\n     * @param ro\n     */\n    public void addRow(Object[] ro) {\n        DefaultTableModel tm = (DefaultTableModel) getTableModel();\n        tm.addRow(ro);\n        fireTableDataChanged();\n    }\n    \n    public void removeRow(int row) { // NO_UCD\n        DefaultTableModel tm = (DefaultTableModel) getTableModel();\n        tm.removeRow(row);\n        fireTableDataChanged();\n    }\n    \n    public void insertRow(int index,Object[] ro) {\n    \t DefaultTableModel tm = (DefaultTableModel) getTableModel();\n         tm.insertRow(index,ro);\n         fireTableDataChanged();\n    }\n\n    public void moveRow(int from,int to) {\n   \t DefaultTableModel tm = (DefaultTableModel) getTableModel();\n        tm.moveRow(from, from, to);\n        fireTableDataChanged();\n   }\n\n    /**\n     * @param event\n     */\n    public void tableChanged(TableModelEvent event) {\n        NewTableModel tm = (NewTableModel) getTableModel();\n        tm.fireTableChanged(event);\n        fireTableDataChanged();\n        \n    }\n\n    /**\n     * @param i\n     */\n    public void setRowCount(int i) {\n        DefaultTableModel tm = (DefaultTableModel) getTableModel();\n        tm.setRowCount(i);   \n        fireTableDataChanged();\n    }\n\n    /**\n     * @param i\n     */\n    public void sortByColumn(int i) {\n        setSortingStatus(i, ASCENDING);\n        \n    }\n\n   \n    public int getMappedIndex(int index) {      \n        return( modelIndex(index));\n    }\n\n   \n    public boolean isSorted() {\n        \n        return isSorting();\n    }\n\n    /**\n     * \n     */\n    public void sort() {\n        fireTableDataChanged();\n    }\n}"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/UIErrorHandler.java",
    "content": "/*\nThis file is part of BORG.\n \n    BORG is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n \n    BORG is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n \n    You should have received a copy of the GNU General Public License\n    along with BORG; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n \nCopyright 2003 by Mike Berger\n */\n\npackage net.sf.borg.ui.util;\n\nimport net.sf.borg.common.ErrorHandler;\nimport net.sf.borg.common.Warning;\n\nimport java.util.logging.Logger;\n\n/**\n * UI error handling for Borg\n */\npublic class UIErrorHandler implements ErrorHandler {\n\t\n\tstatic private final Logger log = Logger.getLogger(\"net.sf.borg\");\n\n\t/**\n\t * Output an exception to the user.\n\t * \n\t * @param e\n\t *            the e\n\t */\n\t@Override\n\tpublic void errmsg(Exception e) {\n\n\t\t// treat a warning differently - just show its text\n\t\tif (e instanceof Warning) {\n\t\t\tnotice(e.getMessage());\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// log the error\n\t\tlog.severe(e.toString());\n\n\t\t// dump the stack trace to stderr\n\t\te.printStackTrace();\n\t\t\n\t\tScrolledDialog.showError(e);\n\n\t}\n\n\t/**\n\t * output a notice/warning - just shows text\n\t * \n\t * @param s\n\t *            the text to show\n\t */\n\t@Override\n\tpublic void notice(String s) {\n\n\t\tScrolledDialog.showNotice(s);\n\t}\n\n}\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/java/net/sf/borg/ui/util/package.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n<head><title>package details</title></head>\n<body>\n  UI Utility Classes\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/BorgHelp.hs",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE helpset PUBLIC \"-//Sun Microsystems Inc.//DTD JavaHelp HelpSet Version 1.0//EN\" \"http://java.sun.com/products/javahelp/helpset_1_0.dtd\"><!--generated by JHelpDev Version: 0.27-03/03/04, see jhelpdev.sourceforge.org-->\n\n<helpset version=\"1.0\">\n <title>\nHelp\n </title>\n <maps>\n  <homeID>\ntop\n  </homeID>\n  <mapref location=\"default/Map.jhm\"/>\n\n </maps>\n <view>\n  <name>\nTOC\n  </name>\n  <label>\nTOC\n  </label>\n  <type>\njavax.help.TOCView\n  </type>\n  <data>\ndefault/BorgHelpTOC.xml\n  </data>\n </view>\n <!--\n <view>\n  <name>\nSearch\n  </name>\n  <label>\nSearch\n  </label>\n  <type>\njavax.help.SearchView\n  </type>\n  <data engine=\"com.sun.java.help.search.DefaultSearchEngine\">\nJavaHelpSearch\n  </data>\n </view>\n -->\n</helpset>\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/JavaHelpSearch/.cvsignore",
    "content": "DOCS\nDOCS.TAB\nOFFSETS\nPOSITIONS\nSCHEMA\nTMAP\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/BorgHelpTOC.xml",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE toc PUBLIC \"-//Sun Microsystems Inc.//DTD JavaHelp TOC Version 1.0//EN\" \"http://java.sun.com/products/javahelp/toc_1_0.dtd\"><!--generated by JHelpDev Version: 0.27-03/03/04, see jhelpdev.sourceforge.org-->\n\n<toc version=\"1.0\">\n <tocitem text=\"Main\" target=\"index\"/>\n\n <tocitem text=\"The Calendar\" target=\"mainscreen\"/>\n\n <tocitem text=\"Editing Appointments\" target=\"editing\"/>\n\n <tocitem text=\"ToDo's and the To Do List\" target=\"todo\"/>\n\n <tocitem text=\"Task Tracking\" target=\"tasktracker\"/>\n\n <tocitem text=\"Address Book/Birthdays\" target=\"address\"/>\n\n <tocitem text=\"Databases\" target=\"db\"/>\n\n <tocitem text=\"Email Reminders\" target=\"email\"/>\n\n <tocitem text=\"Categories\" target=\"categories\"/>\n\n <tocitem text=\"Memos\" target=\"memos\"/>\n <tocitem text=\"Internationalization\" target=\"internationalization\"/>\n\n <tocitem text=\"Options\" target=\"options\"/>\n\n <tocitem text=\"Pop-Up Reminders\" target=\"popups\"/>\n\n <tocitem text=\"Searching\" target=\"search\"/>\n\n <tocitem text=\"Printing\" target=\"printing\"/>\n\n <tocitem text=\"Import/Export\" target=\"xml\"/>\n\n <tocitem text=\"Links and Attachments\" target=\"links\"/>\n\n <tocitem text=\"Encryption\" target=\"encryption\"/>\n</toc>\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/Map.jhm",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE map PUBLIC \"-//Sun Microsystems Inc.//DTD JavaHelp Map Version 1.0//EN\" \"http://java.sun.com/products/javahelp/map_1_0.dtd\"><!--generated by JHelpDev Version: 0.27-03/03/04, see jhelpdev.sourceforge.org-->\n\n<map version=\"1.0\">\n<mapID target=\"top\" url=\"index.html\"/>\n\n<mapID target=\"search\" url=\"search.html\"/>\n\n<mapID target=\"index\" url=\"index.html\"/>\n\n<mapID target=\"todo\" url=\"todo.html\"/>\n\n<mapID target=\"editing\" url=\"editing.html\"/>\n\n<mapID target=\"address\" url=\"address.html\"/>\n\n<mapID target=\"categories\" url=\"categories.html\"/>\n\n<mapID target=\"memos\" url=\"memo.html\"/>\n<mapID target=\"internationalization\" url=\"internationalization.html\"/>\n\n<mapID target=\"mainscreen\" url=\"mainscreen.html\"/>\n\n<mapID target=\"xml\" url=\"xml.html\"/>\n\n<mapID target=\"popups\" url=\"popups.html\"/>\n\n<mapID target=\"options\" url=\"options.html\"/>\n\n<mapID target=\"db\" url=\"db.html\"/>\n\n<mapID target=\"tasktracker\" url=\"tasktracker.html\"/>\n\n<mapID target=\"email\" url=\"email.html\"/>\n\n<mapID target=\"printing\" url=\"printing.html\"/>\n\n<mapID target=\"links\" url=\"links.html\"/>\n\n<mapID target=\"encryption\" url=\"encrypt.html\"/>\n</map>\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/address.html",
    "content": "<HTML>\n<HEAD>\n\n</HEAD>\n<BODY>\n<H2>Address Book/Birthdays</H2>\n<P>The address book is pretty self-explanatory. It\n    holds addresses and other personal information.<BR><BR>There is a\n    birthday field for each record in the address book. If a date is\n    entered in this field, the person's birthday will appear on the\n    calendar on the appropriate day each year. In addition, the person's\n    age will appear on the calendar also.\n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/categories.html",
    "content": "<HTML>\n<HEAD>\n</HEAD>\n<BODY>\n<H1>Categories</H1>\n<P>The Categories feature provides a way to\nmaintain “virtual calendars” within a single database</P>\n<P>From the <I><B>Categories</B></I> menu on the\nmain screen, categories can be created and filtered. Each appointment\nor task can be assigned to a category.&nbsp; The program can then\nfilter tasks and appointments using a user-selected list of\ncategories. Appointments and tasks that are in a category that is\nbeing shown behave normally and appear in the calendar, printouts,\ntask manager, etc... Appointments and tasks that are in a category\nthat is hidden, will remain in your database, but will not appear\nanywhere on any screen as long as their category is not being shown.\nThis provides a way to manage multiple &quot;virtual&quot; calendars\nin one database</P>\n<P>The <I><B>Choose Categories to Display </B></I>menu\noption will allow you to select which categories are visible</P>\n<P>New categories must be added using the <I><B>Add\nNew Category</B></I> menu option before they appear as a choice in\nthe appointment editor or task editor</P>\n<P>The <I><B>Remove Unused Categories</B></I> menu\noption will search all appointments to see which categories are no\nlonger being used and will remove them</P>\n<P>If you use the category feature to partition\nyour calendar into categories, please do not forget which categories\nyou have hidden. Hidden appointments truly do not show up anywhere\nand looking for them can be frustrating. Adding appointments to a\nhidden category and seeing them disappear can also be a bit\nmystifying if you have forgotten your display settings</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/db.html",
    "content": "<html>\n<head>\n\t\n</head>\n<body><p>\n<u><b>Databases</b></u></p>\n<p>Borg\nwill create an SQLite database in a default location when started after\nthe first install. Most users should never alter the database\nsettings. Some users may want to alter the location of the database,\nfor example to be on a removable drive or a cloud drive.</p>\n<p>For those users that want to change the default\nsettings, the available choices are:</p>\n<ul>\n\t<li><p>SQLite</p>\n\t<li><p>H2</p>\n\t<li><p>Generic JDBC</p>\n</ul>\n<p><br/>\n<br/>\n\n</p>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/editing.html",
    "content": "<html>\n<head>\n\t<title></title>\n</head>\n<body lang=\"en-US\" dir=\"ltr\"><h2>Editing\nAppointments</h2>\n<p>When the\nappointment editor first appears, all of its fields are reset to\ndefault values. A\nnew appointment that can be saved by hitting the Save button. \n</p>\n<p>If the day being edited has\nexisting appointments, they can be edited by clicking the\ncorresponding appointment in the appointment list on the right side.\nThe Save button saves any data for the appointment being edited.</p>\n<h3 >To\nDo's</h3>\n<p>If the To Do checkbox is selected,\nthe appointment will be marked as a To Do. It will appear on the To\nDo list window, and can be marked as done from the To Do window also.</p>\n<h3>Repeating\nAppointments</h3>\n<p>The frequency pulldown allows you\nto set the frequency of an appointment that repeats. \n</p>\n<p>Note that there are two types of\nmonthly appointments: \n</p>\n<ul>\n\t<li><p>ones that repeat on the same\n\tdate each month (i.e. the 6th of each month)</p>\n\t<li><p>ones that repeat on the same\n\tday and relative week each month (i.e. the third Thursday of each\n\tmonth). With this type, if no such day exists for a given month, the\n\tappointment is skipped for that month, but the repeat count is still\n\tdecremented.</p>\n</ul>\n<p>When deleting repeated\nappointments, Delete One Only and Delete buttons offer the choice of\neither deleting just the one occurrence of the repeat on the current\nday being edited, or all of the occurrences.</p>\n<p>FYI - repeats are only shown on the\ncalendar for 2 years from the present date. So if you repeat\nsomething yearly for 1000 years and try to look ahead to the year\n3000, you will be disappointed.</p>\n<h3>Repeating\nTo Do's</h3>\n<p>Like\nany other appointment, Repeating To Do's will appear multiple times\non the month view. However, they will only appear once on the To Do\nlist - starting with occurrence number one. If a repeating To Do is\nmarked as done on the To Do list, the NEXT occurrence of the To Do\nwill then be placed on the To Do list - this is done until the last\noccurrence has been marked as done.</p>\n<h3>Vacation,\nHalf Day, Holidays</h3>\n<p>The vacation, half day, and\nholidays check boxes are simply used to mark the day in a different\ncolor on the main month view so that they stand out. They have no\nother effect. (half-day refers to a half-vacation day). A count of\nthe number of vacation days taken for the year (including the current\none) is shown for each vacation day.</p>\n<p>BORG can add certain US and\nCanadian holidays. This is an option from the preferences screen.\nBORG will also &quot;guess&quot; as to whether or not a US holiday\nshould be marked using the holiday color or just normal day color.\nThis is based on whether or not the holiday usually causes a day off\nfrom work in the US. So, for example, New Year's Day would be marked\nin weekend color, but Valentine's Day is not. \n</p>\n<p>Whether or not you use the US\nholiday feature, if a certain day is a holiday for you, you can add\nan appointment with the name of the holiday and mark the Holiday\ncheckbox. If you don&rsquo;t like the default holidays, you can shut\nthem off completely and enter your own.</p>\n<h3>Private\nAppointments</h3>\n<p>Appointments marked as private\nusing the private checkbox only appear in the main month view and\nprint output if the <i><b>Options/Show Private </b></i>checkbox is\nselected from the <i><b>Options</b></i> menu. In this way, Private\nappointments can be hidden when the calendar is used in a public\nsetting. Example uses for private appointments would be to record\ncertain doctor's appointments or to keep a log of your illnesses or\nany other private non-work related items.</p>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/email.html",
    "content": "<HTML>\n<HEAD>\n</HEAD>\n<BODY>\n<H2Email Reminders</H2>\n<P>BORG can be set to send an email\nreminder each day for the next day's appointments. The <I><B>options</B></I>\nmenu is where you can enable email reminders. If this feature is\nturned on, BORG will send out a single email reminder of the next\ndays appointments once per day.</P>\n<P>In the email address field you can\nenter a comma separated list of addresses for the reminder email.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/encrypt.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>Encryption</H2>\n<P>Borg supports the optional encryption of Memos\nand Appointments. When chosen, AES symmetric encryption is used to\nencrypt and decrypt text before storing in the database. This\nencryption is meant to be secure enough to protect any sensitive\ninformation.</P>\n<P>Memos and Appointments can be encrypted by\nchecking the Encrypt on Save check box before saving and then\nsupplying the encryption password when prompted. Encrypted items can\nonly be viewed by pressing the decrypt button and supplying the\npassword. The password is the same for all items (see below). \n</P>\n<P>There is an option on the Encryption options\ntab to define how long Borg will remember an entered password. The\ndefault is 5 minutes. This means that once a valid password is\nentered, the password will not have to be re-entered for further\nencryption/decryption operations for 5 minutes. This time length can\nbe set to any value between 0 seconds and 1 year. A value of 0 would\ncause Borg to prompt for the password for every encryption/decryption\noperation.</P>\n<P>Borg uses a single key for all encryption. It\nis kept in a Java JCEKS Key Store file and is protected by a\nuser-chosen password. Encryption cannot be used unless this file is\nset in the Borg Encryption Options. To create an initial Key Store,\ngo to the Encryption Options tab and browse to create a new file.\nBorg will prompt for a password and then create the Key Store with a\nnew random key.</P>\n<P>The Key Store file contains the encryption key\nand is protected by the password that is entered when the file is\ncreated. Whenever Borg needs to encrypt or decrypt data, it will\nprompt for this password. Do not forget this password. If lost, there\nis no way to recover it and any encrypted data can no longer be\ndecrypted. The key store password is never stored to disk in any\nform. \n</P>\n<P>Likewise, do not lose the Key Store file and\nkeep safe backup copies. If you keep the Borg database on removable\nmedia, such as a thumb drive, and mount it on different machines, you\nwill not be able to decrypt secure data unless the key store file is\nalso available to each machine and set in the options. \n</P>\n<P>For maximum security, the Key Store file should\nbe kept separate from the database. A malicious person that gets a\ncopy of your Borg database and/or your saved XML exports will not be\nable to decrypt your protected data without the Key Store file. \n</P>\n<P>Even with the Key Store file, a malicious\nperson will still not be able to decrypt Borg data without knowing\nthe password that was used to protect the Key Store.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/index.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H1>The BORG Calendar and Task Tracker\nHELP</H1>\n<HR SIZE=6>\n<P>This Help system does not list\nevery single feature of BORG. Anything that BORG does that is not\nlisted here is either something obvious, or something that is\nmistakenly missing. \n</P>\n<P>BORG has 5 main parts:</P>\n<UL TYPE=DISC>\n\t<LI><P>The Calendar, which manages\n\tappointments and “to do” items. \n\t</P>\n\t<LI><P>The Task Tracker, which\n\tmanages complex tasks. \n\t</P>\n\t<LI><P>The Address Book \n\t</P>\n\t<LI><P>Memos \n\t</P>\n\t<LI><P>CheckLists</P>\n</UL>\n<P>There is a vast difference between\na “to do” and a Task, and between the “to do” functionality\nof the calendar versus the Task Tracker. A “to do” is simply a\ncalendar event that also will show up on a list of things to do in\naddition to appearing in the calendar. When the user marks an item on\nthe to do list as done, it is removed from the list. That’s all\nthat makes to do’s different from normal appointments, you can mark\nthem as done.</P>\n<P>A Task is quite different. The Task\nTracker functionality actually grew out of the need to track software\ndevelopment tasks and has more in common with a software\nenhancement/bug tracking system than a calendar. The task tracker can\ntrack different tasks through user tunable states. The task types\nincluded with BORG by default include tasks to represent code\ndevelopment, document development, a generic task, and a generic\nissue (i.e. item that needs an answer). These task types and states\ncan be customized. For example, the author of this program has a\nBorg Release task type that contains the associated states and\nsub-tasks involved in producing and releasing this program.</P>\n<P>The Address book and Memo parts of\nBORG were developed to replace the functionality of the Palm desktop\naddress book and memo applications so that Palm users could eliminate\nthe need to use the Palm desktop. As of release 1.7.2, the Palm sync\nfunctionality is no longer supported, but the address and memo\nfeatures remain. The memo feature now supports encrypted memos,\nmaking it very useful for storing passwords and other sensitive data.</P>\n<P>The Checklist feature is pretty\nsimplistic and takes the place of the various Palm and/or handheld\nchecklist apps.</P>\n<P>Please refer to specific help\nsections for more information about the program.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/internationalization.html",
    "content": "<html>\n<head>\n\t\n</head>\n<body><h2>\nInternationalization</h2>\n<p>BORG supports Locales (regional\nsettings) as of release 1.3. The Locale can be switched from the\n<i><b>Options</b></i> screen. Once a new Locale is chosen, a restart\nis required before the new Locale is used. The date formats, month\nnames, and weekday names are supported for all Locales listed.\nHowever, the majority of text strings in BORG come from properties\nfiles. \n</p>\n<p>Except for this Help and some\nREADME files, BORG supports many languages. Translations to other\nlanguages would only require a translated version of the\nborg_resource.properties file that comes in the BORG jar file.</p>\n<p><i>During development, new strings\nare added to the program. However, the translations are not updated\nby the main developer. Therefore, some new text may appear in English\nfor the various locales until new translations are provided to the\ndeveloper.</i></p>\n<p>\n</p>\n<p>There are 3 different categories of\ntext strings shown in BORG:</p>\n<h3>1. Strings that come from the Java Runtime</h3>\n<p>These are the Month and Day names.\nThese strings always appear in the language associated with the\nlocale that is set in the Appearance tab of the preferences window.\nNothing has to be done to translate these strings.</p>\n<p>Just set a new locale, restart\nBORG, and you should see translated month and day names.</p>\n<h3>2. Strings that come from Resource Bundles</h3>\n\n<p>BORG contains a resource bundle in\nits &ldquo;common&rdquo; project that contains the bulk of BORG&rsquo;s\ntext strings. This bundle is delivered in BORG&rsquo;s common.jar\nfile.  Updating this bundle would probably require Java development\nknowledge.</p>\n<h3>3. Strings that come from regular text or html\nfiles inside borg.jar</h3>\n<p>These include the Help text,\nREADME, License, and CHANGES text.</p>\n<p>These come from regular text or\nhtml files in the resource directory. There is currently no support\nin BORG to show different versions of these files based on locale. If\nyou want to change them, just edit them.</p>\n<p>If you want to send me your\ntranslated properties file so that I can include it in the next\nrelease</p>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/links.html",
    "content": "<html>\n<head>\n\n</head>\n<body><h1>Links\nand Attachments</h1>\n<p>The following types of links can be\nadded to appointments, tasks, projects, and addresses:</p>\n<ul type=\"disc\">\n\t<li><p>File Link &ndash; a link to a\n\tfile somewhere in your file system \n\t</p>\n\t<li><p>Attached File &ndash; a link\n\tto a file that BORG will copy into an attachment folder (supported\n\tfor HSQLDB and H2 only).  \n\t</p>\n\t<li><p>URL &ndash; a link to a URL\n\tthat BORG will open in your default browser. \n\t</p>\n\t<li><p>BORG Object &ndash; you can\n\tadd a link to other BORG appointments, tasks, projects, addresses,\n\tand memos. \n\t</p>\n</ul>\n<p>Links for an Appointment are seen\nin the appointment editor.</p>\n<p>Links for a Task, Project or\nAddress are seen on a new Links tab in their respective editors.</p>\n<p>Links can be added, deleted, and\nopened. \n</p>\n<p>Files and URLs are opened according\nto your operating system settings by launching the viewer specified.\nURLs will open in your default browser. When opening a file link,\nBORG will launch the specific viewer for that file type as specified\nin your OS settings (i.e. PDF viewer, XML viewer, Word, Excel, Open\nOffice, etc&hellip;). If no viewer is specified, the file will not be\nopened.</p>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/mainscreen.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY >\n<H2>The Calendar</H2>\n<P>The\nBORG main screen has 4 different views of the calendar: year, month,\nweek, and day. Navigation buttons appear on the top of each calendar\nscreen and are intuitive (Let me define intuitive. When I say\nintuitive, it means that I am describing something that I haven’t\ndocumented yet, and might not document, because it seems to be\nobvious enough. For example the &quot;next&quot; button on the main\nmonth window). \n</P>\n<P>If\nthe current month is being viewed, the current day will appear dark\npink (configurable). Weekends are shaded slightly darker than\nweekdays. Days can also be marked as vacation days, and if so, will\nappear green.<I>All of these colors are\nfully user tunable from the options screens</I>.<BR><BR>To\nadd/remove/update appointments for a given day, just right click on\nthe text area for that day to bring up a menu. Clicking on an\nappointment will let you edit (double-click to edit) or drag it.\nRight clicking on an appointment will bring up menu options specific\nto that appointment.</P>\n<H3>Week\nView/Day View</H3>\n<P>To the\nright of each week is a button that will produce a &quot;week view&quot;\nwhen pressed. The week view is a grid view of the week that plots the\nscheduled appointments for each day according to their start time and\nduration. The start and end range of the grid and the fonts used are\nuser tunable through the options screen. The week view can be sent to\na printer via a menu option. \n</P>\n<P>A\nsingle-day view, similar to the week view, can be obtained by\npressing the date button for a day. The start and end hours of the\nweek view will also be used as the start and end hours for the day\nview.</P>\n<P>Appointments\nthat show up in the time grid as boxes can be resized and moved via\nthe mouse (button 1). Just click on the center of a box to move it,\nor click on the top or bottom edge to resize. Once the move or resize\nis complete, the appointment times will be updated. \n</P>\n<P>Hover\nover an appointment in either view to get the full text of the\nappointment. \n</P>\n<P>Double\nclick on an appointment to bring it up in the appointment editor.\nDouble click outside of any appointment to bring up an editor to add\na new appointment.</P>\n<P>Clicking\nthe right mouse button will bring up a menu. If you do this within an\nappointment, the menu will give you choices specific to the\nappointment. If you right click outside of any appointment, you get\noptions to add a new appointment via the appointment editor unless\nyou have selected a new appointment time range as described below.</P>\n<P>By\nclicking button 1 and dragging the mouse outside of any appointment,\nyou can select a time range for a new appointment. This will appear\nas a green box. Once a time range is selected for a new appointment,\nclicking the right mouse button and selecting the option to add a new\nappointment will quickly add a new appointment in the selected time\nrange. The user will be prompted for the appointment text.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/memo.html",
    "content": "<HTML>\n<HEAD>\n\n</HEAD>\n<BODY>\n<H2>Memos</H2>\n<P>The memo tab on the main window\nallows you to create and edit plain text memos.</P>\n<P>The functionality provided is\nintended to be compatible with Palm Desktop’s MemoPad application. \n</P>\n<P>As of release 1.7.2, the Palm sync\nis no longer supported.</P>\n<P>Memo text can be searched using\nCtrl-F.</P>\n<P>Memos can be encrypted. See the\nEncryption Help Topic for details.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/options.html",
    "content": "<html>\n<head>\n\n</head>\n<body><p>\n<h2>Options</h2>\n<p>Options can be changed from the\n<i><b>Options</b></i> menu.</p>\n<p><br/>\nMost of the options from the\noptions screens are self-explanatory - except:</p>\n<p>Appearance</p>\n<ul>\n\t<li><p>Look And Feel - the combo box\n\twill be populated only with the installed look and feels in your\n\tJRE. Some of the L&amp;Fs from Java are not so appealing with BORG.\n\t</p>\n\t<li><p>Week View Start and End Hour &ndash;\n\tthe range of times shown on both the Week and Day View grids. \n\t</p>\n\t<li><p>Holidays - Most US and\n\tCanadian holidays can be shown (optionally). Except for Christmas,\n\treligious/ethnic holidays are not included - mainly because they are\n\tusually lunar based and harder to calculate - and because they\n\taren't usually a day off from work. These can be added manually\n\tthrough the appointment editor. \n\t</p>\n\t<li><p>Day start and end hour -\n\tcontrols the part of the day shown in the time grid of the week and\n\tday views.</p>\n\t<li><p>Truncate Appointments in Month\n\tView &ndash; this is set by default and causes only the first line\n\tof appointment text to show in the month view. If you want all lines\n\tof multi-line appointments to show in the month view, shut this\n\tfeature off. \n\t</p>\n</ul>\n<p><b>Popup Reminders</b></p>\n<ul>\n\t<li><p>Minutes\n\tbetween reminders for untimed todos: Timed appointments popup up\n\taccording to the Popup Times selected for each appointment. Untimed\n\treminders have no start time, but still need to pop up every so\n\toften to bug the user. This option sets how often these untimed\n\ttodos get a reminder.</p>\n\t<li><p>Show\n\tall reminders in a single window: You can have separate windows for\n\teach popup reminder (like sticky notes) or just as single reminder\n\twindow with a list of items.</p>\n\t<li><p>Show\n\treminders for Tasks, Projects, Subtasks: if set, and the\n\tcorresponding item has a due date, it will get periodic reminders\n\tlike an untimed todo.</p>\n\t<li><p>Birthday\n\treminder days: how many days before a birthday to start showing\n\treminders about it. The birthdays are taken from the Address Book.</p>\n\t<li><p>Popup\n\tTimes: see the popup reminders help \n\t</p>\n</ul>\n<p><b>Database\nInformation</b></p>\n<p>Apply DB\nChange will change the BORG database location and exit the\nprogram. The new location takes effect when the program is restarted.\nNo data is copied and the original database is not removed. If you\njust want to move or copy db files to a new location &ndash; just\ncopy them and change to the new location from the options screen. \n</p>\n<p><b>Email\nParameters</b></p>\n<p>An email message is sent once a day\nonly, and only for the next day's appointments. Provide the name of\nan SMTP server and email address to send to. \n</p>\n<p><b>Miscellaneous</b></p>\n<ul>\n\t<li><p>Show Stack Trace Option &ndash;\n\tif an error occurs and this option is set, a button will appear with\n\tthe error screen to generate a stack trace. This can be used for\n\tdebugging the error and can be sent to the developers. This need not\n\tbe turned on unless debugging is being done.</p>\n\t<li><p>Socket Port &ndash; setting a\n\tvalue here, (2929 is the default), will allow the program to detect\n\twhen another copy is running. If you start a second copy of BORG\n\twhen one is already running, and the socket port is set, the second\n\tcopy will just open the running copy and exit. \n\t</p>\n\t<li><p>Backup Options: Various\n\toptions to have BORG automatically backup your database to a ZIP\n\tfile in a form that can be imported if you loose you data. For most\n\tpeople, this is a critical option to set. Stuff happens and disks\n\tdie. It's important to do automatic backups, unless you are using\n\tsome other means for backup. Backups should be done to a different\n\thard disk form the one where the database is located.</p>\n</ul>\n<p><b>User Color Scheme</b></p>\n<p>The\nTheme pulldown is used to select themes. There are buttons to add and\ndelete themes.</p>\n<p>When\nApply is pressed, the currently set theme is saved with the color\nsettings selected and the color settings are applied.</p>\n<ul>\n\t<li><p>Mark\n\ttodo in month view: the characters in the text box will be prepended\n\tto todo's in the calendar views to make them stand out. The &quot;magic&quot;\n\tvalue &quot;redball.gif&quot; will cause a red ball icon to be used\n\tinstead of text.</p>\n\t<li><p>Draw\n\tGradient in appt boxes: used to shade the color of the appt boxes in\n\tthe day and week views. This should normally be turned on. This\n\toption exists to support slower linux boxes that cannot draw the\n\tgradients quickly enough. Slowdowns when drawing the gradient seem\n\tto be a uniquely linux issue due to poor video drivers in linux for\n\tsome video chips.</p>\n</ul>\n</body>\n</html>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/popups.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>Pop-Up Reminders</H2>\n<P>As of release 1.7.4, reminders can\nbe popped up in a single window containing a list of reminders. This\nis the new default. The prior implementation – a separate popup\nwindow for each reminder is still an option that can be chosen from\nthe Popup Reminders options tab.</P>\n<P>The user can choose the times for\npop-up reminders to appear on a per appointment basis.</P>\n<P>For an appointment, at the chosen\ntimes, a reminder window will popup for the appointment and a sound\nwill be played. If the reminder window is dismissed, it will reappear\nand the next chosen time – unless the option to suppress further\nreminders was chosen when the window was dismissed (if using multiple\nwindows) or unless the reminder item is hidden (when using the\nreminder list).</P>\n<P>When using the single reminder\nwindow, the window will popup when any item in the list hits a popup\ntime. \n</P>\n<P>From the popup window (either\ntype), the item can be hidden to shut off further popups. If the item\nis a todo, it can be marked as done from the popup window.</P>\n<P>The set of selectable times of the\npopups are tunable through the popups tab of the options screen. On\nthis options screen, the set of times are in minutes. A negative\nnumber indicates a reminder that will go off <I>after</I> the\nappointment time.</P>\n<P>There is an option to use the\n“system beep” sound for the popup. Use this if the default sound\ndoes not play properly.</P>\n<P>Untimed appointments do not get\npopup reminders. As of release 1.5.2, any untimed todo’s for the\ncurrent day will get popup reminders every half hour as long as any\npopup times are selected (this popup time is tunable). If popup\nreminders are not wanted for untimed todos, then set the time\ninterval for untimed todo popups to 0 in the options. \n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/printing.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>Printing</H2>\n<P>For some users,\nprinting a month, week, or day view may be the most important part of\nBORG. Some users will print the next few months and use the printout\nas a &quot;daily planner&quot; until they can get back to BORG to\nupdate it online. There is ample room on the printout to jot new\nappointments.<BR><BR>When you first try to print, the Java print\nservice is queried to find a printer that can do the printing. It may\ntake a few seconds before Java scans the printers on your system and\npops up the printer selection box. This delay is normal and\nunavoidable.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/search.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H1>Search</H1>\n<P>The search option\nfrom the <I><B>Action </B></I>menu will bring up a search window.\nThere are a number of search criteria to choose from.</P>\n<P>The search\ncurrently does not allow wildcards.</P>\n<P>Double-clicking\non a search result item will move the calendar to show that item and\nopen the appointment editor for the day containing the item.</P>\n<P>You can perform\nbulk deletes from the search screen. There is a menu option to delete\nselected items. You can select multiple items and then use the menu\noption to delete them all at once. This is a good way to clean up old\nappointments that are not needed.</P>\n<P>You can also\nchange the category of multiple appointments at the same time. \n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/tasktracker.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>The Task Tracker</H2>\n<P>The Task Tracker is used to keep\ntrack of various tasks with defined start and end dates, different\nstatuses, descriptions, and resolutions. \n</P>\n<P>It is up to the user to decide if\nsomething should be a task or regular appointment. To Do Appointments\ncan be used to hold simple tasks that require no special tracking,\nsuch as &quot;pay mortgage&quot; or &quot;take out garbage&quot;. The\ntask tracker is meant for longer running, more complex tasks, such as\n&quot;code new BORG feature&quot;, &quot;design new widget&quot;, or\n&quot;re-floor bathroom&quot; - things that have multiple steps to\ncheck off, that require documentation of a resolution, and/or have\n&quot;sliding&quot; due dates. \n</P>\n<P>There are 3 levels of objects\nmanaged by the task tracker – Projects, Tasks, and Subtasks.</P>\n<P><U><B>Projects</B></U> \n</P>\n<P>A Project is simply a way to group\nrelated projects and tasks. A Project can set an overall due date for\nall child projects and tasks.</P>\n<P>Each project can have any number of\nchild projects and tasks. All child tasks must have due dates on or\nbefore the project due date. A project cannot be closed unless all of\nits child tasks are closed.</P>\n<P>You are not required to use\nProjects. Tasks can still be created that are not part of any\nProject.</P>\n<P>From the Projects tab, you can use\nthe buttons on the bottom of the screen to Add, Delete, Change,\nClose, or Clone Projects.</P>\n<P>From the Project Tree, you can\nright-click to add new projects or tasks.</P>\n<P><U><B>Tasks</B></U></P>\n<P>Tasks are complex activities that\nhave start and end dates, and are created in an initial state\n(default is OPEN) and progress through a user configurable tree of\nstates to reach an end state (default is CLOSED).</P>\n<P>BORG\ncomes with a set of default task types and states just as an example\nso that the task tracker can do something useful after a fresh\ninstall.</P>\n<P>If\nyou don't like the default task types and states, you can change\nthem. Please see the section below on Editing Task Types and States\nfor more info. The default task states are some of the ones used by\nthe author and provide a good example of what can be done. There is\nno problem if you discard them all and replace them with your own.</P>\n<P>When\nediting tasks, much of the functioning of the editor window is\nobvious. However:</P>\n<P>The PA (person assigned) field\ndoesn’t really do anything except hold data. Use it as you like.</P>\n<H3><U>Subtasks</U></H3>\n<P>Subtasks are single units of work\nthat do not progress through a set of states. They are either\ncompleted or not-completed.</P>\n<P>Subtasks are managed from the same\nwindow used to edit a single tasks. Each subtask can have an optional\ndue date. All subtasks must have due dates on or before the parent\ntask’s due date. A task cannot be closed unless all of its subtasks\nare closed.</P>\n<P>\n</P>\n<P>There are a number of places where\nright-click will bring up a useful menu: the task table for a\nproject, the sub-task table for a task, the project tree, the\nprojects and tasks tabs.</P>\n<P>Also, sub-tasks do not have their\nown form-based screens. Sub-task fields are edited directly in the\nsub-task table. Make sure that you press Save after making changes to\nthe sub-task table.</P>\n<H3><U>Editing Task\nTypes and States</U></H3>\n<P>The user has the capability to edit\nthe list of task types and state transitions in the task tracker. \n</P>\n<P>There is now an editor to help you\nedit the task types and states. It can be reached by choosing the\nfirst menu item under the options-&gt;Task State Options menu. \n</P>\n<P>The task state editor shows task\ntypes, states, next states, and subtasks (from left to right). If you\nselect a task type on the left, the states list will reflect the\nstates for that task type. If you select a state, the next state list\nwill show the list of states that you can transition to from the\nselected state. If you select a task type, the subtask list will show\nthe <I><B>default</B></I> subtasks for that task type. These default\nsubtasks are generated for all tasks of that type and are added when\nthe task is created. These default subtasks are optional. Tasks do\nnot need to have any default subtasks. \n</P>\n<P>To change anything, just select\nwhat you want to change and right-click on it. For example, to add a\nnew state for a task type, select the task type and right click in\nthe state column. One of the options that pops up will be to add a\nstate.</P>\n<P>* I realize that the documentation\non the task type editor is not really that great</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/todo.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>The To Do List</H2>\n<P>The To Do list is accessed from the <I><B>Action/To\nDo</B></I> menu choice from the main Month screen. This list shows\nall appointments marked as To Do's that have not been marked as done.\nTo Do's can be marked as done from the To Do window menu. There are 2\noptions to pick from when marking To Do's as done. The <I><B>Done (No\nDelete)</B></I> menu option, removes the appointment from the To Do\nlist, but leaves it on the calendar. The <I><B>Done (Delete)</B></I>\noption removes it from the calendar also (unless the todo has more\nrepeats, in which case the next occurrence is shown on the to do\nlist).</P>\n<P>When a To Do is marked as done, it is shown\nwith a strike-through font, like this: <STRIKE>done</STRIKE></P>\n<P>There\nis an option that hides all strike-through items on the calendar to\nreduce clutter.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/default/xml.html",
    "content": "<HTML>\n<HEAD>\n\t\n</HEAD>\n<BODY>\n<H2>Import/Export</H2>\n<P>BORG can\nexport/import its data to/from XML. The import/export actions can be\nfound on the month view Action menu. Import/Export provides a good\nway to backup your database in human readable/editable form.</P>\n<P>Users are\nstrongly encouraged to turn on the automatic backup feature which\nexports the entire database to a time-stamped zip file when borg\nshuts down.</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/resource/CHANGES.txt",
    "content": "BORG 2.0.0\n----------\nAdd ability to sync the database to google drive from within BORG\nRemove CALDAV sync\nRemove HSQLDB\nUpgrade H2 database to 2.0\nRemove Dorkbox system tray and use the default tray for Linux\nDo not sync private todos  to the google todo calendar\nAdd Tasks and Subtasks to the items synced to the \"todo calendar\" on google that was created by enhancement #223\n\nBORG 1.10.07\n------------\nAdd ability to export all appointments to ICS files by year - for manageable importing into google or other calendars\nShow week number in week view (feature request) (Issue #235)\nNext Todo occurrence marked as done on month view, but not on Todo list (Issue #227)\nImport from backup XML does not work for Sqlite databases since 1.10.05 #233\nsubtasks syncing to google on the wrong day (Issue #234)\nUpdate 3pp versions\n\nBORG 1.10.06\n------------\nRemove the default JGoodies Look and Feel - Action menu not displayed properly - Win 10 (Issue #224)\nAdd a mechanism to share Todos on google with other users since google Tasks cannot currently be shared (Issue #223)\nUnable to set font style to bold with new font chooser #222\nTasks created in google are added to BORG repeatedly #221\nFix google sync to properly set title/notes of task that has multi-line text\nFix logic that handles google non-repeating events that span multiple days\nIgnore cancelled events from google calendar during sync\nUpdate 3pp versions\n\nBORG 1.10.05\n------------\nMake SQLITE the default database for brand new installs. Does not affect current users.\nMake SQLITE-specific DB option tab requiring only the folder, not a jdbc url\nAdd option to scale icon size\nFix - System tray menu not appearing #205\nSome UI suggestions #202\nSecure the Caldav and Email passwords via the keystore #197\nfix address fields where home/work were reversed\nUpdate the help to clarify some items\nRemove CARDDAV import from server - not supported by 3pp\nProperly order the D/W/M/Y icons in the toolbar\nMove option to hide strike-through items to the View menu\nMake year view interactive instead of read-only\nSwitch to use a better font chooser\nupdate 3pp versions\n\n\nMultiple fixes for google sync\n\nBORG 1.10.04\n------------\nChange Taskbar implementation to fix Linux Mint (and possibly other OS) taskbar issues\nadd View menu to quickly show/hide Private appts, subscribed calendars, and future todo repeats\nAdd ability to subscribe to additional google calendars (read-only)\nFix Shutdown Issue (was Backup issue) #184\nSubtask table readability changes\nTurn on FK validation for SQLite\nImproved sync of tasks to google (and other misc google improvements)\nshow full appt text in tooltips\nadd past due tasks to reminder email\nFix problem when changing look and feel - #193\nMisc fixes\nSecurity updates for 3pp libs\n\n\nBORG 1.10.03\n------------\nAdd new \"Quiet\" option on the reminder list to suppress periodic popping of the window to the front and grabbing focus\nSync tasks and subtasks to google tasks - one way\nAdd separate option to turn daily emails on/off to allow manual emails to be sent without the automatic daily ones.\nUpdate 3pp versions\nThe popup reminder pop up timing is not accurate #169\nStop setting Letter paper size during printing #163\nError and shutdown if a program is running on BORG's socket port but doesn't respond with \"ok\".\ncode cleanup\nFuture repeating appointments not showing more than 2 years in advance #161\nSupport recurring tasks created in aCalendar+ and synced to google by parsing the incoming RRULE in the notes\n\nBORG 1.10.02\n------------\n12 hour format in some places when 24 hour format option is on #158\nget rid of hard-coded colors in the font chooser #157\nGoogle sync fixes\nclean up lots of old code\n\n\nBORG 1.10.01\n------------\nJava 17\nWrong recurrence saved when importing biweekly todo created on aCalendar+ (android) #139\nSimplify backup options\nRemove ical server\nadd option to resend daily email reminder\nfixes for dark themes\nallow type change for tasks in initial state\n3pp upgrades\ndisplay project-less tasks in the task tree\nVCARD import/export from file\nVCARD import from CARDDAV server\nDisplay appointment colors in search results\nAllow bulk color change for appointments in search results\nSync with Google Calendar and Tasks (experimental)\nSupport Sqlite Database\nIncrease address field sizes\ncleanup old code\nremove Mysql specific DB options\nmisc fixes\n\n\nBORG 1.9.04\n-----------\nAdd more themes (look-and-feel). Support Dark themes. #137\nAdd past due todos to reminder email #135\nFix where the \"For repeating ToDo's, only show the current instance\" option is loaded from when displaying the options UI. #134\ncompleting tasks in aCalendar+ tasks (Android) doesn't properly update BORG for repeating todos #133\ndisplay a notification if delete or delete one only is pressed in the appt editor and no appts are selected #115\npopup a notification if search returns no results #112\nadd option to only show the current todo for a repeating todo #106 - fix\nWrong recurrence saved when importing biweekly todo created on aCalendar+ (android) #139\n\nBORG 1.9.03\n-----------\ngetting an ical parse exception when dragging a valid ics file onto Borg #109\ndone no-delete fails to update caldav correctly #107\ncopy and paste of appt doesn't sync correctly to caldav #108\nadd option to only show the current todo for a repeating todo #106\ncaldav sync doesn't handle recurrences of Every N days/weeks/months/years #101\nWindows can be created off-screen if monitor configurations change #99\ndo not include strike-through items in email reminder #98 \n\nBORG 1.9.0.2\n------------\nJava 15\n#88 - fix the SSL self-cert code\nremove dynamic loading (of java libs)\nremove IzPack and Launch4j - Switch to simple jpackage install\n\nBORG 1.9.0.1\n------------\nJava 13\nFix #70 - add change events for subtask deletes when task is deleted. Also prevent\nsubtask updates when there are no changes in order to stop unnecessary caldav syncs\nCalendar Printing Broken in Java 11 #79 - work around added for jdk bug\nadd subtasks to search results, add task dates to search results, add CSV export for search results\nTasks fail to sync to Borg if they lack a start date (#81)\n#82 - upgrade to official ical4j-connector. remove proxy support\nSynced task fails to complete #83\nset property needed for ical4j libs when importing ical appt from file.\n\nBORG 1.9\n--------\nSupport Java 10\nSupport embedded JRE\n[#77] Timed Todo is synced without time to caldav\n[#76] - Timed todo shows up twice in the popup reminder list - and one has the wrong time\n#71 - Appt change date option doesn't sync correctly to caldav\n\nBORG 1.8.3\n----------\n#69 - update next_todo properly when moving a repeating todo\n#68 - when retrieving caldav events, always query with a time range instead of getting all\nSet Frame minimum size properly\nFor Mac OS, use the L&F as set in the user preferences, unless it is a jgoodies L&F.\nAllow non standard ports for caldav server - #63\nupgrade to izpack 5, use maven izpack plugin so that install of izpack is not required to build the installer\nuse launch4j maven plugin to create windows .exe so that a separate launch4j install is not required\nfor windows, include a launch4j program launcher borg.exe for better windows support\nBug Fixes\n\nBORG 1.8.2\n----------\nAdd unlimited color choices for appointments\nAdd caldav sync of Tasks/Subtasks (outgoing only)\nAdd optional periodic DB flush (close/reopen)\nFix bug when completing a periodic todo from Android\nAllow SSL connections for CALDAV\nAllow switching between a list of reminders or individual post it notes without requiring a restart\nlots of miscellaneous bug fixes - see github for the full list\n\n\nBORG 1.8.1\n----------\n- IMPORTANT: This release provides support for Java 8.\n- IMPORTANT: This release requires Java 7 or higher.\n\nMany Caldav-sync improvements including support for owncloud\nAdd new repeat option - Monthly Day (Last)\nMany minor bug fixes\n\n\nBORG 1.8\n--------\nAdd Support for sync with CALDAV Server (Baikal)\nIcal fixes to support export/import between BORG and Android ICSSync\nSwitch to maven/Reorganize project\nMerge Ical plugin into base product\nOption to FTP an Ical export on shutdown\nAdd pending Projects, Tasks, Subtasks with due dates to Ical export\nImport Ical from URL\nRemove Mnemonics\nRemove Address Phone Number and Email Validation\n\nClosed Tickets\n==============\nType\t#\tSummary\nDefect\t377\tAdding a SubTask without selecting a Task does not result in the creation of a SubTask.\nDefect\t376\tAttempting to add a Subtask without selecting a task does not invoke an error.\nDefect\t374\tAttempting to Sync displays an error indicating that sync is not on in the Ical properties, but it is unclear as to how to turn sync on.\nDefect\t360\tJdbcSQLException upon copying a checklist and naming it with > 50 letters\nEnhancement\t356\tChecklist renaming allows input of more than 50 characters\nDefect\t350\tDuplicate Holiday entries for the same *.ics file\nEnhancement\t345\tLocal language list is not ordered\nDefect\t338\tOpening colour setting and clicking cancel changes original colour to black\nDefect\t328\tHelp menu URLs do not function properly, and causes mouse malfunctions\nDefect\t327\t�Tasks� and �Project� can not properly handle �Move Following Day� and �Change Date�\nDefect\t326\t�Tasks� and �Project� can not do �Edit� and double click in �To Do List� window\nDefect\t325\tAppointment editor right-click menu on delete is misleading\nDefect\t322\t\"Clean Due Date\", \"Set Due Date\" and \"Delete\" don�t work.\nDefect\t307\tSave button shifts to the right as the length of the description increases\nEnhancement\t304\tCategory wizard window disappears after asking for input\nDefect\t303\tEditing appointments with vacation unchecked in properties part\nDefect\t302\tRepeat to find the same row in \"Address Book\"\nDefect\t301\tDelete in appointment editor always prompts Confirm Delete\nDefect\t297\tUser can copy checklist into a list with no name\nDefect\t296\tGetting a JdbcSQLException when entering a long name for a new memo\nDefect\t292\tAppointment search result contains description attached in item name\nDefect\t285\tMemo import fails upon importing Entire Backup Zipfile on v1.8\nDefect\t283\tjdbcSQLException error occurs upon importing Entire Backup Zipfile on v.18\nDefect\t280\tThe \"Search\" feature in the Help section does not work or takes too long to find a given term.\nDefect\t278\tAlt + L, Alt + S, Alt + E and Alt + o doesn't have any effect under Appearance even though it has underlined L, S, E, o indicating a mnemonic.\nDefect\t277\tAlt + R doesn't have any effect in the Reminder Email Time under Email Parameters even though it has underlined R indicating a mnemonic.\nDefect\t276\tAlt + S doesn't have any effect in the SMTP Server under Email Parameters even though it has underlined S indicating a mnemonic.\nDefect\t275\tconflict �Alt-h� in the edit preferences\nDefect\t274\tPressing \"Alt-q\" does not activate the \"Frequency\" menu in the Appointment Editor.\nDefect\t273\tWhile importing �.ics� calendar file, the chosen category for imported appointments is reset to �No Category� after the import\nDefect\t272\tCannot Remove Empty Item in Checklist\nDefect\t269\tCanceling \"Quick ToDo\" window shows the same warning of empty textbox\nDefect\t268\t\"mark todo in month view\" checkbox marks also day view and week view.\nDefect\t267\tUsing up button when setting 'Times' value in Recurrence doesn't work for 0\nDefect\t255\tchecklist name with more than 50 characters causes SQL exception\nDefect\t252\t\"Uncheck All\" button doesn't produce a warning when performed with no checklist selected.\nDefect\t243\tPressing \"Alt-r\" does not open \"Categories\" menu, even though it contains an underlined r (indicating \"r\" mnemonic).\nDefect\t242\tPressing \"Alt-o\" does not open \"Options\" menu, even though it contains an underlined o (indicating \"o\" mnemonic).\nDefect\t241\tPressing \"Alt-c\" does not open \"Action\" menu, even though it contains underlined \"c\" (indicating \"c\" mnemonic).\nDefect\t240\tDragging an event off the top of the month view and releasing left mouse button throws the event into the previous month.\nDefect\t237\tThe due date is earlier than the start date of the SubTask\nDefect\t236\tspace � � can be used to add an appointment in Quick ToDo list\nDefect\t234\t\"Search String\" fails for \"Memo\" when \"Memo\" has more than one line\nDefect\t233\tSpace \" \" can be used as description/summary for creating a project/task.\nDefect\t219\tTask State Editor does not allow creation/modification of new/existing Task Types\nDefect\t218\tValid Home Phone and Work Phone numbers not accepted in Address Book\nDefect\t217\t\"Null Pointer Exception\" thrown when trying to import backup appointments from version 1.7.9 into version 1.8 (Compatibility Issues)\nDefect\t216\tA task can be brought back to OPEN state even its Project is in CLOSED state\nDefect\t214\tA Project can be closed before its sub projects (with open tasks) can be closed.\nDefect\t213\tA cyclic loop can be created between a project and its parent causing the looped parts to disappear from the �Project Tree�.\nDefect\t209\tDeleted Projects still appear in Parent drop down menu as options\nDefect\t206\tSpace \" \" can be used as Appointment Text for appointments.\nDefect\t204\tChanging the date in �Appointment List� on the �Appointment Editor� view changes the date the appointment gets added to, even though the title of the �Appointment Editor� tab still reflects the original date.\nDefect\t202\tUsing �Save & Close� with an undocked �Appointment Editor� causes the selected docked view in the main application window to close.\nDefect\t201\t\"Category\" drop down menu under the \"To do List\" tab keeps populating with repeated categories.\nDefect\t199\tcan�t change an appointment�s category to <no category>\nDefect\t197\tChanging the date of the appointment using \"Change Date\" checkbox in appointment editor throws \"Null Pointer Exception\"\nDefect\t196\tThrows Null Pointer Exception when Recurrence End Date is left Empty for an appointment.\nDefect\t195\tHistory field of a task does not log change in due date when task is moved in the Month view\nDefect\t193\tInconsistent behavior while copying encrypted appointments\nDefect\t192\tUncheck all option for checklists does not remove line through item name\nDefect\t190\tRenaming of a memo with an existing memo deletes the original memo\nDefect\t187\tCannot edit entry in the address book ('Entry Exists')\n\nBORG 1.7.9\n----------\n\nFeatures:\n=========\n174\tFile Chooser does not remember previously accessed directory and open up that\n168\tAddress book \"Street Address\" field too short\n98\tSelecting the time for appointments in the day/?week view\n50\tAddress Book accepts invalid email and phone formats\nMake popup reminder color tunable (post-it view only)\nAdd ability to drop ical file onto borg (ICAL plugin)\n\nBug Fixes:\n==========\n186\tDouble-clicking in day view times column results in irreversible graphical glitch\n184\tStartup View option not working\n183\tCategory with empty string as name can be created\n172\tCopy and Paste allows us to violate recurrence rules\n171\tAn encrypted appointment should be labeled in the link dialog\n170\tEmpty URL behavior in Address Book\n167\tExporting xml prefs overwrites existing file without confirmation\n166\tLinking an address to itself results in a double link\n164\tThe application accept empty or a white space character as URL Link.\n163\tFor BORG Link, the adding and deleting BORG Link behavior are inconsistent.\n162\tIf we input an empty string as the URL link, and open this URL from Links section, the application incorrectly opens an OS window let us to select file.\n161\tWhen an appointment \"apt2\" link itself as the BORG link, the BORG Links Appointment[apt2] are shown twice in the Links section.\n160\tA private appointment can be select as \"BORG\" link, and also can be edit in \"BORG\" link.\n158\tInvalid priority in xml import accepted\n157\tImport of an appt with an incorrect date causes null pointer\n156\tAddressBook allows duplicate entries\n153\tExport should not be enabled for encrypted Memos\n152\tNo feedback for the \"Print\" function from task view\n149\tRepeating instances are calculated incorrectly for �month(day)� repeat type\n148\tAppointment become invisible when start date is after the repeat until date\n147\tDeleting an opened Task leaves the opened tab, but removes it from the TaskList\n146\tMove repeating appointment by dragging failed after the first occurrence got deleted\n142\tSpace character (\" \") accepted as text for appointments\n141\tCheckList should not allow deleting all items in a list\n139\tRe-naming an existing Memo to more than 50 characters deletes the Memo\n138\tRe-naming an existing Checklist to more than 50 characters deletes the checklist\n134\tSearch features fails on specific date search (not inclusive)\n131\tEnd Date can be before Start date in Search Feature\n129\tNotify User When They Have No Categories To Delete\n124\tappointment created on a leapyear (Feb 29) stops recurring on the next leapyear\n61\tMenu graphical glitch\nFix popup text for untimed items (post-it view)\n\n\nBORG 1.7.8\n----------\n\nFeatures:\n=========\n6\tAllow active HTML links in memos\n14\tAdd summary field for tasks - separate from description\n20\tAppointment Copy/​Paste on calendars\n28\tAllow delete of Memos from search window\n71\tShow User The Path To Exposed iCal File (plugin change)\n74\tHide duplicate holidays when showing both American and Canadian holidays\n79\tFilter Unsupported File Extensions From iCal Export (plugin change)\n111\tAdd a Cell Phone Field to the Address Book\nAdd log viewer\nUpdated Polish Translation\nAdd close date to task list\nAdd rename option for memos and checklists\n\nBug Fixes:\n==========\n15\tChange Date on appt editor is broken\n16\tFails to Update User When Calender Cannot Be Imported\n17\tFilter Unsupported File Extensions From XML Import\n19\tError when trying to close a task from the todo list\n22\tAppointment start time can be after end time (negative duration)\n23\tAppointment with empty subject\n26\tAddress books accepts names of only blank characters\n27\tAppointment editor can accept invalid minutes input for appointment time\n29\tTimes of recurrence in appointment editor truncates large input without give error /​ warning.\n30\tFuturistic birthday dates are allowed in address book\n31\tCategory can not be changed when create new to do in the To Do view\n32\tsubtask under a project can start early than its parent project\n34\tCategory names greater than 10 characters not accepted in Projects\n35\tInvalid time in appointment editor leads to invalid value message to be displayed twice\n37\tSetted appointment dispare when change from every n days to once\n40\tDragging and dropping a Todo to the edge causes it to go to the wrong spot\n42\tFont chooser dialog doesn't resize and the title has a typo\n43\tBehavior of inputting new category name is inconsistent.\n44\tThe ‘Save’ button in appointments should clear subject field\n49\tAddress Book throws uncaught SQL Exception\n52\tCannot select other created category in Search window\n55\tUndoing a deletion of an appointment does not restore links\n56\tFilter Unsupported File Extensions From iCal Import\n57\tDragging of headings displays blank data in the reminder pop up window\n58\t'Person Assigned' field in 'Task Information' throws uncaught SQLException\n59\tA Task with an open Sub-Task can be closed\n64\tMemo deleted even when Done(No Delete) was choosen\n68\tAll unused categories are also deleted after deleting one of selected category.\n69\tUncaught java.lang.NullPointerException\n70\tImporting 'Entire Backup Zip File' or 'XML file' generates org.h2.jdbc.JdbcSQLException\n72\tUnable to delete subtasks from the 'Tasks' view\n73\tDeleting multiple 'File Links' from a Project throws an Exception\n75\tThe \"note\" text box does not warp around to the next line\n76\tUnable to see all options after changing 'Look and Feel' to certain profiles\n77\tClose\" button does not work in the add category panel \n78\tTo-Do List Copying Error: Repeating discard message\n80\tUsing 'print' command only shows from '2nd day' of the month for months that begin on a sunday\n81\tBeing able to \"Export\" with no memo selected\n84\tAble to create a Checklist item with white space as title\n85\tUser can create a Memo with an empty name\n86\tProgram freezes if an extremely large Memo is created\n87\tUncaught parsing error while importing preferences\n88\tUncaught \"SQL Exception\" on long address when adding a contact\n89\tDuplicate hot-keys in \"address book entry\" panel\n91\tContradictory to specification  KEYSTORE can be created with an empty password\n93\tExport option shows \"open\" instead of \"save\" in the save window\n95\tContradictory to the specification  the \"WHOLE WORD\" option of the search menu still considers 'empty strings' and 'whitespaces' as whole words.\n96\tUncaught java.lang.NullPointerException\n99\tSpace being considered as case sensitive and a whole word in the search function\n112\tDay/Week buttons don't work on the month view\nprevent \"java.lang.SecurityException: Access to default session denied \" when sending email\t\nclear editor when clicking on root of project tree\t\nfix some threading issues with the shutdown splash screen\t\nupdate quick-todo and search view category menus when categories are added\t\n\n\nBORG 1.7.7\n----------\n\nFeatures:\nRe-layout Task, Project, Address, and Search forms to make better use of space\nHide links table if there are no links to save space\nPrepend task description to subtask on calendar\nUse H2 as default database instead of HSQL\n#12 Allow 1 minute increments for appointment times\n#11 Edit appointment duration by setting the end time\n#10 Allow setting of the views that open at start up\n#9 Show colors to indicate tasks approaching the due date\n#34 (trac)\tallow different From and To addresses for email reminders\n#36 (trac)\tUpdated Spanish (Argentina) Translation\n\nBug Fixes:\n#13 Move Box on Week View shows wrong end time\n#8 Start date and Due date are reversed on the Project View\n#37 (trac)\tError creating DB with Java 7\n#35 (trac)\tWrong item dragged when there are too many untimed events to fit in a calendar box\n#32 (trac)  A new blank subtask is not added to the subtask table automatically\n\nBORG 1.7.6.3\n------------\n\nFeature #25: Spanish Translation Updates\n\nBug #21: Daylight Savings Time\nBug #22: Color themes do not refresh when importing from XML\nBug #23: Theme name is too big for H2, MySQL\n\nOther #24: Add logging to Borg with a debug option\n\nBORG 1.7.6\n----------\nBug and Feature numbers now come from Trac\n\nFeature #7 repeat pattern (By N weeks, months, years)\nFeature #10 Standard Menu Bar for all Windows\nFeature #9 Daily view scale setting - add zoom buttons on day and week view. does not affect printing.\nFeature #6: add reminders for tasks\nFeature #12: Show popup reminder for birthdays\nFeature #15: Color Schemes and support for dark themes\nFeature #16: Allow user to type in a look and feel name\nFeature #19: Allow user to change system tray appearance. Fix System Tray sizing.\nFeature #20: Add a database modification timestamp to detect linux remount errors\nFeature: add option to select a particular looks color theme when using a looks look and feel.\nFeature (contributed code): Add text to days in year view\nFeature: add ability to open any view from the tray icon menu\n\nBug #3 category not consistant across borg clients\nBug #2 Can't print Task List\nBug #4 borg help not loaded in 1.7.5\nBug #1 Linux desktop shortcuts not always created\nBug #17 Fix the initial focus and save&close mnemonic on the appt editor screen\nBug Fix start-to-tray to properly create the month view tab\n\nOther: Clean up the build script. Compile by Package to enforce package dependencies.\nOther: Add an optional IVY task to download the latest third-party lib versions\nOther: create separate \"ext\" folder to contain dynamically loaded libs (plugins, added l&f's)\nCode Refactoring: eliminate used of restricted APIs. Use lombok to clean up Bean classes. Remove dead code.\n\n\nBORG 1.7.5\n----------\nFeature 3118440 - Checklists\nFeature 1416675 - To Do Priorities\nFeature 3136779 - Ability to Import Backup Zip all at once\nFeature 3116794\t- Make all tabs dockable\nFeature 3084552\t- Ask for confirmation when memo is going to be deleted\nFeature 3075832\t- Enable multiple selection in pop-up reminders\nFeature 3227225 - Ability to override the dock/undock defaults per window\nFeature 3217074 - Enhanced set of shutdown/backup options\nFeature (Patch) 3094537\t- \"todo quick add\" field enhancements\nFeature - option to email the backup ZIP when it is created\nFeature - Export XML changed to create a single ZIP file instead of separate XML files\nFeature - Option to Sort by priority on calendar\nFeature - Support H2 Database\nFeature - anti-alias text\nFeature (Patch) - total number of items for TODO and Task lists\nFeature - Whole Word Search\nFeature - updates to the Italian Translation\nFeature - option to show subtasks in task lists\nFeature - in project tree, show empty projects as folders, not leaves\n\nBug 3076434\t- Backdated ToDos-Birthdays pop up for each year until present\nBug - memo view does not clear currently shown memo when the tab is closed.\nBug - options do not reset to stored values if the option view is closed and reopened.\nBug 3217092 - BORG does not remember dock state after closing\nBug - do not create undo records when importing\nBug - only show first line of appt text in email reminders\nBug 3285186 - fix exe installer for windows-7\n\nBORG 1.7.4\n----------\nSpanish (Argentina) Translation\nFeature - add search in memo text\nFeature - [3039608] Single pop-up window for multiple reminders\nFeature - [3059547] Add Repeat Until as a Recurrence option\nFeature [2945673] - add back start to system tray feature\nFeature - option to filter closed tasks in project/task tree\nFeature - option to filter closed tasks in project task list\nFeature [2960249] - option to hide status and task number in task tree display\nFeature - automatically detect a few more Look and Feels\nFeature [2945434] add option to show date in tray icon\nFeature [2985309] Single click activate, double click add appointment\n\nBug - Make Daylight Savings Time Change entries non-clickable\nBug - Fix import bug that messes up links. Also speed up import.\nBug - change color chooser buttons so that color is always shown. Was lost for some look and feels.\nBug [2934430] - add buttons to increase/decrease the size of the non-timed area for day and week views.\nBug - make todo table react to changes in user colors preference\nBug [2985307] ToDo list always appears at startup\n\nOther - Move reports and GANTT chart out of BORG. Create an unsupported plugins project\nto contain the reports, GANNT, and the old Ical import.\n\nBORG 1.7.3\n----------------\nFeature [2893839] Add optional encryption for Memos and Appointments\nFeature [2913708] extend search to cover all types\nFeature - encrypt stored email password\nFeature - allow user to provide their own reminder sound\nFeature - set a default db on start up for new install. Do not prompt for DB options.\n\nChange - Set default JGoodies theme to Experience Blue\nChange - Hide report items if report libs not available\nChange - Hide Gantt buttons if JFreechart not available\nChange - Move UI logic out of Borg class. Move UI logic out of non-Swing thread.\nChange - improve font options screen\nChange - split up OptionsView class into smaller modules\n\n[Bugs-2897010] Appt drag on month view works wrong if week starts with monday - only if first day of month is a sunday\nBug - Links to Borg Objects not deleted when target is deleted\nBug - Linked Memos do not open properly\nBug - Splash screen text is editable\nBug - NPE if popup timer goes off during shutdown after db is closed\nBug - Missing scrollbar in Appointment Panel\nBug - task filter missing on task list screen\nBug - appt boxes do not show strike-through text\n\nBORG 1.7.2 - Developer Release\n-----------------------------------\n** Feature [2846844] Code Cleanup and Javadoc **\n** The entire code base was cleaned up, refactored, and documented in this release.\n** Thousands of lines of obsolete code have been removed.\n** The UI code was rewritten to remove almost all of the IDE-generated code.\n\nFeature [2769671] Add an address book search  \nFeature [2852664] support TLS for SMTP (for gmail)\nFeature [2876229] rewrite XML import/export to use JAXB\nFeature [2876230] improve week/day view\nFeature [2876231] add quick-note menu option\nFeature [2876232] put the 3 task tabs under a single main tab\nFeature [2876242] Remove Palm sync Support\nBug Fix [2876233] cleanup the translations - remove unused strings\nBug Fix [2876234] tab close/undock icons do not highlight\nBug Fix [2876236] clean up the online help\nBug Fix [2876237] popups sometimes continue after appts changed or deleted\nBug Fix [2876238] when deleting multiple addresses - the wrong ones are deleted\nBug Fix [2876240] fix calendar view tooltip\nBug Fix [2745960] pop up reminder non-functional at large-minute settings \nBug Fix [1728135] No Popups--- Date Doesn't Advance\nBug Fix [1693757] Category name size not checked\n\n** BORG 1.7.2 now requires Java 6.\n  \nBORG 1.7.1\n----------\nFeature [2648806] add an undo feature\nFeature [2234537] add quick-todo option to calendar context menus\nFeature [2221223] backup to ZIP file\nFeature [2216864] add task list to project view\nFeature [2626455] add option to change SMTP port\nFeature [2628166] highlight tab icons on mouse-over\nFeature [2628187] highlight the link tab on tasks if links are present\nBug Fix [2465525] RunSQL missing commit\nBug Fix [1988106] Overlapping widgets on recurrence panel\nBug Fix [2628138] don't send empty reminder email  \n\n\nSimple BORG 1.7\n-----------------\nFeature 2198214  Create Simple BORG \nFeature 2198199  Add option to hide strike-through items\nFeature 2198196  Add tray icon option to hide all reminders\nFeature 2198193  Make sure quick todos take fields from the default appt\nFeature 2198191  display count of vacation days per year\nFeature 2198189  Enhance New appointment box\nFeature 2198182  Add ability to search for items with links\nFeature 2198179  Makes tasks/projects/subtasks editable from calendar UIs\nFeature 2198173  Convert all code to use Generics\nFeature 2198172  Remove ical/vcal support\nFeature 2198165  Remove multi-user features\nFeature 2198160  Use Java 6 system tray\nFeature 2198157  Upgrade to Java 6  \n\nRemove about 1/3 of the code and lots of non-core features.\nRemoved: multi-user, remote server, local files db, ical/vcal, and many rarely-used smaller features\n\n\nVersion 1.6.1 Final\n-------------------\nFeature [1904725]  mark todos as done from reminder popup\nFeature [1903141]  Add marker on calendar if appt has links  \nFeature [1871712]  Show past non-timed todo reminders\nBug Fix [1959692]  can't drag appt from non-timed to timed  \nBug Fix [1951636]  cant change save a task after first entered\nBug Fix [1933587]  deleted repeats show up in todo list and popups\nBug Fix [1909656]  month and year view missing 1st\nBug Fix [1886138]  ClassCastException when savong changed subtask\nBug Fix [1873225]  Add New from Day/Week view broken in 1.6.1 b3\n\nVersion 1.6.1 Beta 3\n--------------------\nFeature [1851556] add links/attachments\nFeature [1820023] Links and hyperlinks\nFeature [1595707] Add Address Book linking with dates\nFeature [1653120] Add \"Year View\" tab/display\nFeature [1671535] Date/time-stamp for Memos\nFeature [1856643] improve SQL output \nBug Fix [1856661] Program loses track of window size after maximize\nBug Fix [1856590] Run SQL runs too slow \nBug Fix [1855501] Time Zone Problem\n\nVersion 1.6.1 beta 2\n--------------------\nFeature [1833863] allow dragging of appts in the month view  \nFeature [1842095] add sub projects\nFeature [1833864] allow dragging of appts outside of grid in day/week views\nFeature [1834235] import/export of preferences  \nFeature [1834573] automatic backup\nFeature [1841894] display task and subtask duration column\nFeature [1841893] change subtask creation date to start date\nBug Fix [1844200] Todo marker icon does not scale\nBug Fix [1844197] Text todo marker functionality accidentally removed\nBug Fix [1844194] day and week print is meesed up in 1.6.1 beta  \nBug Fix [1833869] user colors not used in new views\nBug Fix [1834857] error when emailing meeting notice \nBug Fix [1619520] today marker color doesn't update \nBug Fix [1842126] Task import bug\nBug Fix [1840005] Palm Conduits will not work with Hotsync 6\nBug Fix [1839373] Project status / localized value  \n  \n\nVersion 1.6.1 Beta\n------------------\nFeature [1830421] new month view  \nFeature [1826911] add a toolbar  \nFeature [1778929] GANTT Charts\nFeature [1825699] make some of the windows dockable \nFeature [1826487] add a tree based project browser\nFeature [1829289] add todo marker to day and week views \nFeature [1827264] allow appts to be dragged between days on the week view      \nFeature [1730886] add automatic db update for hsqldb \nFeature [1730718] support private memos\nFeature [1799708] memo: warn on unsaved changes  \nFeature [1716211] allow undated tasks\nFeature [1824596] send email errors to a window  \nFeature [1829365] obsolete old features \nFeature [1831526] todos incorrectly marked as done in month print   \nBug Fix [1781575] reminder popups show for deleted appts  \nBug Fix [1777793] do not validate dates of closed tasks/subtasks\nBug Fix [1746887] null pointer error when chging task due date\nBug Fix [1738531] task state editor hangs\nBug Fix [1730918] private todo's are not private on the Palm\nBug Fix [1719016] some files missing from install\nBug Fix [1714949] system tray errors in Linux\nBug Fix [1787412] Palm Appt Conduit Performance bug  \nRefactor code into a single Eclipse Project (except for palm and servlet). Reorganize Packages.\n\nVersion 1.6\n-----------\nAdd Portuguese (Brazilian) translation\nFeature [1664278] Add \"copy to date\" option in Appointment Editor\nFeature [1663823] Add day-of-week name to header of \"Day\" view\nFeature [1656975] Options for \"ToDo\" icon?\nFeature [1651947] Tasklist to OpenOffice Calc Export\nFeature [1628049] memo to txt combined with txt2tags\nBug Fix [1704593] repeating todo bug \nBug Fix [1690078] conditional formating is lost on columns \nBug Fix [1690074] project creation dates do not check overlaps\nBug Fix [1668450] Partial character crop in name display of Address Book\nBug Fix [1666260] Remote Mysql server connection problem\nBug Fix [1657333] Todo View does not refresh when subtask closed\nBug Fix [1656972] No \"Project\" or \"Tasks\" display w/ some \"Look/Feel\" options \nBug Fix [1655548] java.lang.Exception: Error: No initial state found for task\nBug Fix [1654751] NullPointerException from TodoView.java\n\nVersion 1.6 beta 2\n------------------\nFeature [1646015] enhance remote db to support tasks\nFeature [1645758] use project values to initialize new task\nFeature [1645750] allow task navigation and close from todo list\nFeature [1645720] add projects to calendar\nFeature [1644941] display subtasks on todo list\nFeature [1651552] improve look of week/day view\nFeature [1644928] add option to turn off task numbers in cal & todo list\nBug Fix [1646013] project import fails\nBug Fix [1633184] Error with MySQL 4.0\nBug Fix [1629517] can close task with open subtasks\nBug Fix [1629514] save task incorrectly detects date change\n\n\nVersion 1.6 beta 1\n------------------\nFeature [1109652] - Redesign Subtasks in Task Tracker to allow unlimited subtasks per task, each with its own due date\nFeature [1109652] - Add Projects. A Project contains a collection of related Tasks.\nFeature [1593551] - Add a Task Log to record Task status changes\nFeature [1617531] - Add Simple Plain-Text memos, compatible with Palm-Desktop\nFeature [1617531] - Add Palm Hot-Sync conduit for the new memos\nFeature [1618416] - Move the Task Tracker into the main window as 2 new tabs - Projects and Tasks\nFeature [1622984] - Make HSQLDB the default and deprecate Local Files\nFeature [1358885] - Separate the appointment title and description in the editor\nFeature [1616972] - Update the date chooser widget (which will also fix some bugs caused by the prior one)\nFeature [1592065] - Support Skipped Repeats on the Palm\nFeature [1590836] - Store all BORG appt fields on the Palm to prevent editing appts on the Palm from clearing certain fields\nBug Fix [1619741] - fix up the default colors, which were ugly and hard to see at times.\nBug Fix [1619498] - fix up the behaviour of tables and add row striping\nBug Fix [1600804] - sort elapsed time numerically\nBug Fix [1590982] - prevent changes to a repeating appt or todo from resetting the list of skipped days and done occurrences when possible\nBug Fix [1590834] - Palm sync should not lose the appt color\n\n*** Deprecated the Local Files Database. HSQLDB is now the default ***\n\nVersion 1.5.2\n-------------\nHSQLDB Standalone Database Support\nAllow navigation and access to the appt editor from day and week views\nCombine Month/Day/Week views into one tabbed window\nAllow graphical appt editing from the day and week views\nAdd full appt text on mouse-over to day and week views\nAdd half-hourly reminder popups for the day's untimed todos\nMake fonts in printouts, day, and week view tunable\nMake the popup reminder times tunable\nAdd option to shutoff system tray\nUpgrade iCal libraries\n\nVersion 1.5.1.2\n---------------\nShow as many days from prior and next month as will fit in the current month view\nSocket Interface - available commands: shutdown, sync, open\nUpdated German Translation - (also in 1.5.1.1)\nTrack Closed Date for Tasks\nTrack Elapsed Time for Tasks\nTimed Individual Reminder Emails for Appointments \n\nVersion 1.5.1\n-------------\nAbility to open multiple shared (public) calendars when using remote server\nKeyboard Shortcuts\nFulltext tooltips when hovering over days in the month view\nTodo and Appt quick change date and move to next date actions\nTodo and Appt List right click context menus\nAdd date chooser for birthdays\nFlemish (Dutch Belgian) Translation by Rudy Defrene < sat2freak@users.sourceforge.net>\nRepeat by user-selected days of the week\nTomcat-based data server\nImproved Search\nBulk Delete and Change Category\nFix some major Palm Sync Bugs\nAdd PRI column to Task List View\nAdd word-wrap to week view\nSupport both case sensitive and insensitive filtering/searching\nUpgrade to JGoodies Looks 1.3.2\nSMTP authentication support\nMYSQL 5.0 compatible\n\nVersion 1.5\n-----------\nImport iCal into a particular category (1.5 final only)\nDelete Category including appts and tasks (1.5 final only)\nPalm Synchronization Conduits\nImproved Popup reminders with user tunable popup times per appointment\nAdd date chooser to task GUI\nShare categories between appointments and tasks.\nChange to use category pull-down menu on task editor\nAllow GIF images to mark todos in calendar\nSimplified Chinese Translation\nDutch Translation\nRemove Logging feature\nSwitch to JDIC for Windows and Linux Tray Icon Support\nAbility to send reminder email on demand for any day\nOption to show repeat occurrence number\nMulti-user DB auto-sync\nTunable reminder email time\nOption not to truncate appointment text at one line\nEmail to multiple addresses\nTunable line-spacing in month view\nBug fixes\n\nVersion 1.4.2\n-------------\nPolish Translation from Rafa? Gawro?ski\nUser tunable colors for the month view and todo list\nExport to vCalendar 1.0 (.vcs) format (used by Palm Desktop)\nSave Address or Task information as HTML using XSLT\nImproved Todo GUI\nHighlight todo's on the month view\nShow done todo's with strike-through\nAdd day navigation from appointment editor\nOther miscellaneous GUI improvements\nOption to export iCal in UTC (for outlook)\nRepeat every N days\nBug fixes\n\nVersion 1.4 patch 1 (1.4.1)\n---------------------------\nA GUI to edit the tracker's task types and states\nScreens will remember the size and position set by the user\nSupport large numbers of categories (>100)\nOption to show day of year numbers\nBug Fixes\n\nVersion 1.4 final\n--------------------\nItalian Translation from Piero Trono http://php-multishop.com tropic@users.sourceforge.net\nAdd Bulk Delete from Search Results Menu\nAdd a new Help System based on JavaHelp\nAbility to save default values in the appt editor\nRepeat by Mon-Wed-Fri and Tues-Thurs\nImproved week view layout that handles overlapping events\nDay View\nRemove trayicon classes from borg.jar and bundle in lib dir instead\nAdd Options option to the trayicon\nAdd better MySQL support to make it much simpler to use MySQL\nBug Fixes\n\nVersion 1.4 beta\n---------------------\nbundle the jgoodies look and feels with the product\nimport/export iCal format\nnew type of monthly repeat by week/day of week\njump from search and todo list to appt editor\nrefactoring of the model and database code\nimprovements to reduce volume of JDBC activity between client and db\nAbility to run as a demo applet (\"sandboxed mode\")\nadd \"apply\" button for options - no more immediate apply\nbug fixes\n\nVersion 1.3.2\n---------------------\nUse color when sorting appts\nAdd -trayname option\nExport/Import to URL\nAllow multiple BORGs to open and share the same database file\nGerman Translation\nEliminate remaining US date formats\nAdd Week Numbers\nbug fixes\n\nVersion 1.3.1 \n----------------------------------\n- ADD categories to Todo List\n- ADD options to view README and CHANGES files\n- redo the appointment editor to have an appointment list showing all of a day's appts.\n  allow the user to select any appt on the list to edit.\n- allow full-blown font selection for the default font, appointment font, and preview font\n- Reorganize the code into packages. Refactor most of the code, except for NetBeans generated code.\n- Support the full Print Option Dialog provided by Java, which includes options for paper size, page orientation, copies, etc...\n- Support printing of 1-12 months at a time. Most printer drivers can then provide 2-sided printing and 2-up printing.\n- Add a birthday field to the address book. Use this field to include birthdays on the calendar, along with age.\n- Support unlimited repeats for appointments with a new checkbox on the appointment editor\n- add 2 more repeat frequencies\n- make the preferences window smaller by using tabs\n- make the popup reminder functionality tunable from the preferences window\n- make the splash screen optional\n- bug fixes: fix some window layout problems where windows would display too narrow.\n             fix bug that sometimes messed up the main window look and feel whenever the options menu was opened.\n             fix character encoding problems - make database and XML files UTF-8\n             fix the first/last name problem in the address book\n             fix appointment editor bug when repeat freq does not match times - bad db record is stored\n\n             \n\nVersion 1.3 \n-----------\n- contents of 1.3 beta 1 & 2\n- Spanish Translation\n- ability to change locale from the options screen\n- eliminate lock files/switch to java.nio locking\n\nVersion 1.3 beta 2\n--------------------------------\n- appointment categories/virtual calendars\n- resource files for internationalization\n- more tray icon menu options\n- allow XML import without KEYs\n- todo quick-add\n- ability to move appointments from one date to another\n- ability to change the type of a task in progress\n- bug fixes\n\nVersion 1.3 beta 1\n--------------------------------\n- Add Jdbc database layer - experimental\n- Add Address Book\n- Add Task Categories\n\nVersion 1.2\n--------------------------------\n- Add User-Defined Print Logo to month view\n- Add import from XML and improve export to XML\n- Standardize Date format in the database to make it portable\n  across incompatible locales\n- Add Repair tool to MDB\n- Add support for Windows systray\n- Add Canadian Holidays\n- Add word wrap option for month view and month print\n- Add navigation menu\n- Add task save button\n- Bug fixes\n\nVersion 1.1 beta\n--------------------------------\nWeek View and Print\nAppointment Durations\nBetter support for non-scheduled appts from appt editor\nMajor code cleanup. Rewrite of much of the class model.\n  - Redo all classes. Redo Model, View, Controller\n  - Create new BeanDB Database object to use instead of SMDB\n  - BeanDB will use a schema defined in XML\n  - a class generator will also use the schema to generate Bean classes at\n    build time\n  - BeanDB will only export the generated Bean classes to the outside world\n  - the generated classes will be Appointment and Task\nAllow Database dir switch without program restart\n\nVersion 1.0\n-----------\n- First Day of Week = Monday\n- 24 hour time format\n- bug fix for missing day contributed by Mark Wrangham (ptigga@users.sourceforge.net)\n- allow change of LookAndFeel to any installed LookAndFeel or any in\n  classpath.\n- allow system subtasks to be tunable\n- fix bug where 0:00 showed up as the time for a non-scheduled appt\n\nVersion 0.9.4\n-------------\n- Print Preview\n- Background color added to color print\n- Auto Update check - optional feature to periodically check - BORG website for latest release number so users do not have to check for new releases.\n- Preview of Todos and Tasks on the main window\n- some code cleanup. One or two bug fixes.\n\nVersion 0.9.3\n-------------\nadded autostart feature\nmade task types and states tunable - a sizable new feature\nimproved help screen\nadded comments, comments, and more comments to the source\nfixed 2 popup bugs - see bug tracker for info\n\nVersion 0.9.2\n-------------\n- Add 5 user tunable checkboxes per task to track subtasks\n- Fix bug where beeping popup would continue beeping after the window was closed.\n\nVersion 0.9.1\n-------------\n- Auto population of most US holidays\n- XML export and logging\n- Audio notification of imminent appointments\n\nVersion 0.9\n-----------\nInitial Release on SourceForge\nRemoved all non-GPL code\n\nAncient History\n---------------\n8/2001 - Initial Java version\n7/1994 - Initial Tcl/Tk/C++ version\nEarly 90's - Original X11 Version, C++/Xaw\n\nOriginal Copyright (no longer valid)\n------------------\nThis program is not to be in the possession of anyone except Michael Berger.\nDo not even think of reading the rest of this sentence without explicit \npermission from Michael Berger, unless you are a good friend of Michael \nBerger, or someone who has provided free food for Michael B. Berger.\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/resource/COPYING",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n \n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n \nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n \n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n \n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n \n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/resource/RELEASE_NOTES.txt",
    "content": "==================================\nRELEASE NOTES for BORG 2.0\n==================================\n\nBORG 2.0 is a major release\n\nFunctionality that relied on out of date third party software has been removed.\n\nDO NOT upgrade to BORG 2.0 without reading this.\n\n-----\n\n!!!CALDAV sync is Removed in BORG 2.0. If you rely on Caldav sync, stay on version 1.X!!!\nCALDAV functionality continues to degrade as CALDAV servers receive updates while the third party software used\nby BORG does not.\n\n----\n\n*** DATABASE CHANGES ****\n\nIf you use HSQL or H2 as your database you MUST export your database from BORG 1.0, delete your database file, \nand import your data back into into BORG 2.0 into H2 or Sqlite.\n\nHSQL has been removed from BORG 2.0. BORG 1.X is still runs HSQLDB 1.8 from 2009. HSQL 2.0 (2010) was a complete \nrewrite of HSQLDB that BORG will not support.\n\nH2 has been upgraded to version 2. There is no automated upgrade from H2 v1 (2019) to H2 v2 provided by the H2 software.\nThis is a MANUAL upgrade, which BORG will do when you manually export from BORG 1.X, delete the database and import into BORG 2.0. \n\nSqlite users can upgrade normally.\n\n----\n\nThe Dorkbox system tray that was being used for Linux has been removed. It had problems that were not being addressed.\nThe default Java system tray will be used for Linux even though it is not perfect.\n\n\nLICENSE\n-------\nBORG is open source under the GPL. A copy of the GPL is included with it.\nSource is available from: https://github.com/mikeberger/borg_calendar\n"
  },
  {
    "path": "BORGCalendar/swingui/src/main/resources/resource/borglicense.txt",
    "content": "BORG LICENSE \n\nBORG is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nBORG is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with the BORG source code; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n\nThe BORG source code should have been made available at the site that distributed\nthe binaries. If not, contact the author.\n\nBORG is copyright 2003 by Mike Berger\n\nhttp://mbcsoft.com\n\n\n========================\n\n\nLists of 56 third-party dependencies.\n     (GNU General Public License - Version 2) common (BORGCalendar:common:2.0 - http://mbcsoft.com/common)\n     (GNU General Public License - Version 2) model (BORGCalendar:model:2.0 - http://mbcsoft.com/model)\n     (The Apache License, Version 2.0) FlatLaf (com.formdev:flatlaf:3.7 - https://github.com/JFormDesigner/FlatLaf)\n     (The Apache License, Version 2.0) FlatLaf IntelliJ Themes Pack (com.formdev:flatlaf-intellij-themes:3.7 - https://github.com/JFormDesigner/FlatLaf)\n     (The Apache Software License, Version 2.0) Google APIs Client Library for Java (com.google.api-client:google-api-client:2.8.1 - https://github.com/googleapis/google-api-java-client/google-api-client)\n     (The Apache Software License, Version 2.0) Calendar API v3-rev20251028-2.0.0 (com.google.apis:google-api-services-calendar:v3-rev20251028-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-calendar)\n     (The Apache Software License, Version 2.0) Google Drive API v3-rev20251114-2.0.0 (com.google.apis:google-api-services-drive:v3-rev20251114-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-drive)\n     (The Apache Software License, Version 2.0) Google Tasks API v1-rev20251102-2.0.0 (com.google.apis:google-api-services-tasks:v1-rev20251102-2.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-tasks)\n     (BSD New license) Google Auth Library for Java - Credentials (com.google.auth:google-auth-library-credentials:1.30.0 - https://github.com/googleapis/google-auth-library-java/google-auth-library-credentials)\n     (BSD New license) Google Auth Library for Java - OAuth2 HTTP (com.google.auth:google-auth-library-oauth2-http:1.30.0 - https://github.com/googleapis/google-auth-library-java/google-auth-library-oauth2-http)\n     (Apache 2.0) AutoValue Annotations (com.google.auto.value:auto-value-annotations:1.11.0 - https://github.com/google/auto/tree/main/value)\n     (The Apache Software License, Version 2.0) FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)\n     (Apache-2.0) Gson (com.google.code.gson:gson:2.11.0 - https://github.com/google/gson)\n     (Apache 2.0) error-prone annotations (com.google.errorprone:error_prone_annotations:2.35.1 - https://errorprone.info/error_prone_annotations)\n     (The Apache Software License, Version 2.0) Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.2 - https://github.com/google/guava/failureaccess)\n     (Apache License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:33.1.0-jre - https://github.com/google/guava)\n     (The Apache Software License, Version 2.0) Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)\n     (The Apache Software License, Version 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client)\n     (The Apache Software License, Version 2.0) Apache HTTP transport v2 for the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-apache-v2:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client-apache-v2)\n     (The Apache Software License, Version 2.0) GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:2.0.0 - https://github.com/googleapis/google-http-java-client/google-http-client-gson)\n     (Apache License, Version 2.0) J2ObjC Annotations (com.google.j2objc:j2objc-annotations:3.0.0 - https://github.com/google/j2objc/)\n     (The Apache Software License, Version 2.0) Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.36.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client)\n     (The Apache Software License, Version 2.0) Java 6 (and higher) extensions to the Google OAuth Client Library for Java. (com.google.oauth-client:google-oauth-client-java6:1.39.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client-java6)\n     (The Apache Software License, Version 2.0) OAuth 2.0 verification code receiver for Google OAuth Client Library for Java. (com.google.oauth-client:google-oauth-client-jetty:1.39.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client-jetty)\n     (EPL 1.0) (MPL 2.0) H2 Database Engine (com.h2database:h2:2.4.240 - https://h2database.com)\n     (Eclipse Distribution License - v 1.0) Old JAXB Core (com.sun.xml.bind:jaxb-core:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/)\n     (Eclipse Distribution License - v 1.0) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/)\n     (GNU LESSER GENERAL PUBLIC LICENSE) JCalendar (com.toedter:jcalendar:1.4 - http://www.toedter.com/en/jcalendar/)\n     (Apache License, Version 2.0) Apache Commons Codec (commons-codec:commons-codec:1.11 - http://commons.apache.org/proper/commons-codec/)\n     (The Apache Software License, Version 2.0) Commons IO (commons-io:commons-io:2.3 - http://commons.apache.org/io/)\n     (The Apache Software License, Version 2.0) Apache Commons Logging (commons-logging:commons-logging:1.2 - http://commons.apache.org/proper/commons-logging/)\n     (Apache 2.0) io.grpc:grpc-api (io.grpc:grpc-api:1.70.0 - https://github.com/grpc/grpc-java)\n     (Apache 2.0) io.grpc:grpc-context (io.grpc:grpc-context:1.70.0 - https://github.com/grpc/grpc-java)\n     (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-api:0.31.1 - https://github.com/census-instrumentation/opencensus-java)\n     (The Apache License, Version 2.0) OpenCensus (io.opencensus:opencensus-contrib-http-util:0.31.1 - https://github.com/census-instrumentation/opencensus-java)\n     (EDL 1.0) Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.4 - https://github.com/jakartaee/jaf-api)\n     (Eclipse Distribution License - v 1.0) Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.4 - https://github.com/jakartaee/jaxb-api/jakarta.xml.bind-api)\n     (Common Development and Distribution License (CDDL) v1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp)\n     (GNU General Public License - Version 2 with the class path exception) JavaHelp API (javax.help:javahelp:2.0.05 - https://javahelp.dev.java.net/)\n     (CDDL) (GPLv2+CE) JavaMail API (compat) (javax.mail:mail:1.5.0-b01 - http://kenai.com/projects/javamail/mail)\n     (Eclipse Public License 1.0) JUnit (junit:junit:4.13.2 - http://junit.org)\n     (Apache License, Version 2.0) Apache Commons Collections (org.apache.commons:commons-collections4:4.1 - http://commons.apache.org/proper/commons-collections/)\n     (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.8.1 - http://commons.apache.org/proper/commons-lang/)\n     (Apache License, Version 2.0) Apache HttpClient (org.apache.httpcomponents:httpclient:4.5.14 - http://hc.apache.org/httpcomponents-client-ga)\n     (Apache License, Version 2.0) Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga)\n     (The MIT License) Checker Qual (org.checkerframework:checker-qual:3.42.0 - https://checkerframework.org/)\n     (GNU Lesser General Public License Version 3) Font Chooser (org.drjekyll:fontchooser:3.1.0 - https://github.com/dheid/fontchooser)\n     (EDL 1.0) Angus Activation Registries (org.eclipse.angus:angus-activation:2.0.3 - https://github.com/eclipse-ee4j/angus-activation/angus-activation)\n     (New BSD License) Hamcrest Core (org.hamcrest:hamcrest-core:1.3 - https://github.com/hamcrest/JavaHamcrest/hamcrest-core)\n     (iCal4j - License) ical4j (org.mnode.ical4j:ical4j:2.2.5 - http://ical4j.github.io)\n     (iCal4j - License) ical4j-vcard (org.mnode.ical4j:ical4j-vcard:1.0.1 - http://ical4j.github.io)\n     (The MIT License) Project Lombok (org.projectlombok:lombok:1.18.38 - https://projectlombok.org)\n     (MIT License) JCL 1.1.1 implemented over SLF4J (org.slf4j:jcl-over-slf4j:1.7.14 - http://www.slf4j.org)\n     (MIT License) SLF4J API Module (org.slf4j:slf4j-api:1.7.25 - http://www.slf4j.org)\n     (BSD 3-clause) ThreeTen backport (org.threeten:threetenbp:1.3.6 - https://www.threeten.org/threetenbp)\n     (The Apache Software License, Version 2.0) SQLite JDBC (org.xerial:sqlite-jdbc:3.51.1.0 - https://github.com/xerial/sqlite-jdbc)\n\n     \n==========================\n\n\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n\n\n"
  },
  {
    "path": "BORGCalendar/swingui/src/test/java/net/sf/borg/test/DumpPw.java",
    "content": "package net.sf.borg.test;\n\nimport net.sf.borg.common.PrefName;\nimport net.sf.borg.common.Prefs;\nimport net.sf.borg.ui.util.PasswordHelper;\n\npublic class DumpPw {\n\n\tpublic static void main(String args[]) throws Exception {\n\t\t\n\t\tboolean testing = false;\n\n\t\t// process command line args\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tif (args[i].equals(\"-test\")) {\n\t\t\t\ttesting = true;\n\t\t\t}\n\n\t\t}\n\n\t\t// if testing, use alternate prefs so regular prefs can be left alone\n\t\tif (testing)\n\t\t\tPrefs.setPrefRootNode(\"net/sf/borg/test\");\n\n\t\t\n\t\tSystem.out.println(\"STORE = \" + Prefs.getPref(PrefName.KEYSTORE));\n\t\tSystem.out.println(\"EMPASS = \" + Prefs.getPref(PrefName.EMAILPASS));\n\t\t\n\t\tSystem.out.println(\"Clear EM Pass = \" + PasswordHelper.getReference().decryptText(Prefs.getPref(PrefName.EMAILPASS), \"test\", false));\n\t\t\n\n\t}\n\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# BORG Calendar\n\n## Intro\nBORG is a combination calendar and task tracking system. The calendar supports all sorts of appointments and also a simple todo list. The calendar functionality is similar to that of other PIMs, such as Microsoft Outlook, Mozilla Calendar, Palm Desktop, Yahoo Calendar, etc... The Task Tracker goes way beyond todo's and might better be called a project tracking system.\nIt is a standalone Desktop application.\n## Why BORG?\n\nA potential BORG user might agree with the following: \n\n* I use a calendar/task tracker to remind me to do everything in life that I have to do.\n* I do not want to access the internet to see my calendar\n* Some of my data is private. I don't trust putting it on an internet server.\n* I work on Linux, Windows and other OS's and would like any of my machines to run my calendar program\n* I like open source software. Even if the developer(s) die, I can still build and enhance my calendar program.\n\n## Features\n \n* Month/Week/Day/Year views\n* To-Do list\n* Repeating Appointments\n* Private Appointments that do not show by default (for recording things like \"rectal exam 2pm\" that you don't need your friends/coworkers to see over your shoulder).\n* Pop-up reminders\n* Email reminders\n* A Project/Task/Subtask Tracking System\n* A simple address book\n* XML import/export\n* Memo book with a strong encryption option (for very private memos, passwords, etc..)\n* Checklists\n* more ...\n\n## How to Get BORG\n \nBORG downloads and releases are available on Github: https://github.com/mikeberger/borg_calendar/releases\n\n## How to build BORG\n\n* Some knowledge of Java and Apache Maven is assumed.\n* Checkout the project from Github\n* Have a JDK installed of version 17 or higher. openjdk-17 is currently being used.\n* Import the project into any IDE as a Maven project (optional)\n* Build using Maven from the IDE or by installing Apache Maven and running mvn from the command line (use clean and install targets)\n* To build an executable installer with bundled Java, edit and run the appropriate script in the install folder - winpackage.bat for windows or linpackage.sh for linux\n* The installer is built using the jpackage utility that is part of openjdk. An additional product called WiX Toolset needs to be installed for jpackage to build an msi installer on windows.\n* For a contribution head-start view available [project architecture diagrams](https://sourcespy.com/github/mikebergerborgcalendar/).\n"
  }
]