├── .gitignore ├── .travis.yml ├── README.rst ├── bin └── lib │ └── .gitignore ├── build.xml ├── build_osx.bash ├── docs ├── DocAnchor.class ├── FAQ.html ├── about.html ├── add.png ├── addfood.png ├── addserving.png ├── apple1.png ├── biomarkers.html ├── biomarkers.png ├── blank.html ├── calendar.png ├── copyyesterday.png ├── cron.css ├── daily.png ├── editpreferences.png ├── export.png ├── food.html ├── help.png ├── help.xml ├── index.html ├── introduction.html ├── limitations.html ├── menu1.png ├── menubar.html ├── navtools.png ├── newfood.png ├── notes.html ├── notes.png ├── nutrient.png ├── nutrient1.png ├── nutrition.png ├── readme.html ├── recipe.html ├── release1.0.html ├── release8.html ├── release9.html ├── summary1.png ├── targets.html ├── targets.png ├── tips.html ├── today.png ├── tomorrow.png ├── toolbar.png ├── track.html ├── user_manager.png ├── users.html ├── webversion.html ├── wv.png └── yesterday.png ├── lib ├── LGPL.txt ├── crdb_005.jar ├── jcommon-1.0.10.jar ├── jfreechart-1.0.6.jar ├── swingx-core-1.6.2.jar └── usda_sr28.jar ├── release ├── osx │ └── Contents │ │ ├── Info.plist │ │ ├── MacOS │ │ └── JavaApplicationStub │ │ ├── PkgInfo │ │ └── Resources │ │ └── cronometer.icns └── windows │ ├── cronometer.exe4j │ └── icon.ico ├── scripts └── deprecated.py ├── src ├── ca │ └── spaz │ │ ├── cron │ │ ├── Cronometer.java │ │ ├── ExportWizard.java │ │ ├── actions │ │ │ ├── CopyExercisesAction.java │ │ │ ├── CopyServingsAction.java │ │ │ ├── CopyServingsToUserAction.java │ │ │ ├── CreateRecipeAction.java │ │ │ ├── CutExercisesAction.java │ │ │ ├── CutServingsAction.java │ │ │ ├── DeleteExercisesAction.java │ │ │ ├── DeleteFoodAction.java │ │ │ ├── DeleteServingsAction.java │ │ │ ├── EditFoodAction.java │ │ │ └── ExportFoodAction.java │ │ ├── datasource │ │ │ ├── CRDBFoods.java │ │ │ ├── Datasources.java │ │ │ ├── DeprecatedFoodProxy.java │ │ │ ├── FoodDataSource.java │ │ │ ├── FoodProxy.java │ │ │ ├── JarXMLFoodDataSource.java │ │ │ ├── USDAFoods.java │ │ │ ├── USDAImport │ │ │ │ ├── USDAFood.java │ │ │ │ ├── USDAImporter.java │ │ │ │ └── USDAWeight.java │ │ │ ├── UserFoods.java │ │ │ ├── UserFoodsListener.java │ │ │ ├── XMLFoodLoader.java │ │ │ └── ZipXMLFoodDataSource.java │ │ ├── exercise │ │ │ ├── Exercise.java │ │ │ ├── ExerciseDialog.java │ │ │ ├── ExerciseEditor.java │ │ │ ├── ExerciseEditorListener.java │ │ │ ├── ExerciseHistory.java │ │ │ ├── ExercisePanel.java │ │ │ ├── ExerciseSelection.java │ │ │ ├── ExerciseTable.java │ │ │ └── ExerciseTableModel.java │ │ ├── foods │ │ │ ├── Food.java │ │ │ ├── FoodEditor.java │ │ │ ├── FoodHistory.java │ │ │ ├── FoodSelectionListener.java │ │ │ ├── Measure.java │ │ │ ├── MeasureEditor.java │ │ │ ├── MeasureWidget.java │ │ │ ├── NutrientEditorTable.java │ │ │ ├── NutrientInfo.java │ │ │ ├── NutrientTable.java │ │ │ ├── NutrientTableModel.java │ │ │ ├── Recipe.java │ │ │ ├── RecipeEditor.java │ │ │ ├── Serving.java │ │ │ ├── ServingEditor.java │ │ │ ├── ServingEditorListener.java │ │ │ ├── ServingSelection.java │ │ │ ├── ServingSelectionListener.java │ │ │ ├── ServingTable.java │ │ │ └── ServingTableModel.java │ │ ├── metrics │ │ │ ├── AddMetricDialog.java │ │ │ ├── Biomarker.java │ │ │ ├── BiomarkerDefinitions.java │ │ │ ├── BiomarkerPanel.java │ │ │ ├── BiomarkerPanelOld.java │ │ │ ├── BiometricsHistory.java │ │ │ ├── Metric.java │ │ │ ├── MetricChart.java │ │ │ ├── MetricEditor.java │ │ │ ├── MetricEditorListener.java │ │ │ ├── MetricEditorOld.java │ │ │ ├── MetricSelectionListener.java │ │ │ ├── MetricTable.java │ │ │ └── MetricTableModel.java │ │ ├── notes │ │ │ ├── Note.java │ │ │ ├── NoteEditor.java │ │ │ └── NotesHistory.java │ │ ├── records │ │ │ ├── History.java │ │ │ ├── Record.java │ │ │ ├── RecordSelectionListener.java │ │ │ ├── RecordTable.java │ │ │ └── RecordTableModel.java │ │ ├── summary │ │ │ ├── AbstractNutrientSummaryPanel.java │ │ │ ├── AminoAcidSummaryPanel.java │ │ │ ├── HTMLSummaryFormat.java │ │ │ ├── LipidSummaryPanel.java │ │ │ ├── MacroChart.java │ │ │ ├── MacroNutrientSummaryPanel.java │ │ │ ├── MineralSummaryPanel.java │ │ │ ├── NutrientTable.java │ │ │ ├── NutritionSummaryPanel.java │ │ │ ├── ReportWindow.java │ │ │ ├── SummaryFormat.java │ │ │ ├── TEXTSummaryFormat.java │ │ │ ├── TargetBar.java │ │ │ ├── TargetSummaryChart.java │ │ │ └── VitaminSummaryPanel.java │ │ ├── targets │ │ │ ├── DRI.java │ │ │ ├── DRITargetModel.java │ │ │ ├── NutrientInfoPanel.java │ │ │ ├── Target.java │ │ │ ├── TargetEditor.java │ │ │ ├── TargetEditorTable.java │ │ │ ├── TargetEditorTableModel.java │ │ │ └── TargetModel.java │ │ ├── ui │ │ │ ├── DailySummary.java │ │ │ ├── ReadMe.java │ │ │ ├── SearchDialog.java │ │ │ ├── SearchHit.java │ │ │ └── SearchPanel.java │ │ └── user │ │ │ ├── Favorites.java │ │ │ ├── User.java │ │ │ ├── UserChangeListener.java │ │ │ ├── UserDatePickerDialog.java │ │ │ ├── UserManager.java │ │ │ ├── UserManagerDialog.java │ │ │ └── UserSettingsDialog.java │ │ ├── gui │ │ ├── DateChooser.java │ │ ├── DoubleField.java │ │ ├── ErrorReporter.java │ │ ├── HelpBrowser.java │ │ ├── HyperLabel.java │ │ ├── IconFont.java │ │ ├── IntegerField.java │ │ ├── JMonthChooser.java │ │ ├── JYearChooser.java │ │ ├── PercentageBar.java │ │ ├── PrettyTable.java │ │ ├── PrettyTableModel.java │ │ ├── SpazCanvas.java │ │ ├── SpazCanvasLoader.java │ │ ├── SpazLayout.java │ │ ├── SpazMenuBar.java │ │ ├── SpazPosition.java │ │ ├── StatusBar.java │ │ ├── TransferActionListener.java │ │ ├── TranslucentLabel.java │ │ ├── TranslucentPanel.java │ │ ├── TranslucentToolBar.java │ │ ├── WebViewer.java │ │ ├── WrappedPanel.java │ │ └── WrapperDialog.java │ │ ├── lists │ │ ├── ListListener.java │ │ └── SmartList.java │ │ ├── sql │ │ ├── Columns.java │ │ ├── DBRow.java │ │ ├── SQLColumnSet.java │ │ ├── SQLDelete.java │ │ ├── SQLInsert.java │ │ ├── SQLRow.java │ │ ├── SQLSelect.java │ │ ├── SQLSelectableStatement.java │ │ ├── SQLStatement.java │ │ └── SQLUpdate.java │ │ ├── task │ │ ├── Task.java │ │ └── TaskListener.java │ │ ├── util │ │ ├── BrowserLauncher.java │ │ ├── CacheMap.java │ │ ├── CountableInputStream.java │ │ ├── ImageFactory.java │ │ ├── JarLoader.java │ │ ├── Loader.java │ │ ├── Logger.java │ │ ├── ProgressListener.java │ │ ├── Settings.java │ │ ├── SettingsChangeEvent.java │ │ ├── SettingsChangeListener.java │ │ ├── StringUtil.java │ │ ├── ToolBox.java │ │ └── XMLNode.java │ │ └── wizard │ │ ├── Wizard.java │ │ └── WizardPanel.java ├── com │ └── apple │ │ └── mrj │ │ ├── MRJAboutHandler.java │ │ └── MRJQuitHandler.java ├── docs │ ├── issues.html │ └── readme.html ├── fontawesome.tff ├── img │ ├── apple-100x100.png │ ├── apple-50x50.png │ └── icon.png ├── menubar.xml ├── nutrients.xml ├── se │ └── datadosen │ │ └── component │ │ └── RiverLayout.java └── todos.txt └── start_cronometer.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .*.swp 3 | /bin/ca/ 4 | /bin/com/ 5 | /bin/se/ 6 | /lib/cronometer.jar 7 | /lib/docs.jar 8 | CRONoMeter.app 9 | /tags 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_script: 4 | - curl -o ant.tar.gz https://www.apache.org/dist/ant/binaries/apache-ant-1.10.6-bin.tar.gz 5 | - tar xf ant.tar.gz 6 | 7 | script: 8 | - PATH="$PATH:$PWD/apache-ant-1.10.6/bin" ./build_osx.bash 9 | - ls ./CRONoMeter.app 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | cronometer 3 | ========== 4 | 5 | .. image:: https://travis-ci.org/myint/cronometer.svg?branch=master 6 | :target: https://travis-ci.org/myint/cronometer 7 | :alt: Build status 8 | 9 | `cronometer` is a nutrition tracking tool. This is a fork of the original_. 10 | 11 | .. _original: http://sourceforge.net/projects/cronometer 12 | 13 | 14 | Build on OS X 15 | ============= 16 | 17 | :: 18 | 19 | $ ./build_osx.bash 20 | 21 | 22 | 23 | Build and start on Linux 24 | ======================== 25 | 26 | :: 27 | 28 | $ ant 29 | $ ./start_cronometer.sh 30 | 31 | 32 | Download 33 | ======== 34 | 35 | https://github.com/myint/cronometer/releases 36 | 37 | 38 | Importing new USDA food database 39 | ================================ 40 | 41 | As an example, below is the procedure that was used to upgrade from ``SR26`` to 42 | ``SR28``. 43 | 44 | Unzip the old processed database:: 45 | 46 | $ unzip lib/usda_sr26.jar 47 | $ mv usda_sr26 usda_sr28 48 | 49 | Update ``src/ca/spaz/cron/datasource/USDAImport/USDAImporter.java`` to point 50 | to ``sr28``. 51 | 52 | Run the importer to update the old processed data:: 53 | 54 | $ java -classpath lib/cronometer.jar \ 55 | ca.spaz.cron.datasource.USDAImport.USDAImporter < sr28.zip 56 | 57 | Append deleted items from the old ``foods.index`` into the new 58 | ``deprecated.index``:: 59 | 60 | $ ./scripts/deprecated.py usda_sr26/foods.index usda_sr28/foods.index \ 61 | >> usda_sr28/deprecated.index 62 | 63 | Create the new JAR:: 64 | 65 | $ rm lib/usda_sr26.jar 66 | $ zip -r lib/usda_sr28.jar usda_sr28 67 | 68 | Update ``src/ca/spaz/cron/datasource/USDAFoods.java`` to point to the new JAR. 69 | And update the OS X app to point to the new JAR. 70 | -------------------------------------------------------------------------------- /bin/lib/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/bin/lib/.gitignore -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Builds the Cronometer application. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /build_osx.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | ant 4 | 5 | root="$(dirname "$0")" 6 | app="$root/Cronometer.app" 7 | rm -rf "$app" 8 | mkdir "$app" 9 | cp -r "$root/release/osx/Contents" "$app" 10 | 11 | mkdir -p "$app/Contents/Resources/Java" 12 | cp -r "$root/lib"/* "$app/Contents/Resources/Java/" 13 | -------------------------------------------------------------------------------- /docs/DocAnchor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/DocAnchor.class -------------------------------------------------------------------------------- /docs/FAQ.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Frequently Asked Questions

This section answers some 17 | frequently-asked questions. 18 | 19 |

One of the foods has an error in the nutrients, what should I 20 | do?

Cronometer has 3 databases: 21 | 22 | We recently learned that unsalted almond butter (USDA 12195) 34 | had a Vitamin E amount zero, whereas salted almond butter (USDA 35 | 12695) had the correct amount of Vitamin E. Inconsistencies in 36 | the USDA database may eventually be corrected by the government, 37 | but by editing the data yourself, you can use more accurate data 38 | immediately. Any deficiencies in the databases can be corrected 39 | by editing the food, saving it in "My Foods", and then using the 40 | edited food in your recipes or daily menus. 41 | 42 |

I recently got a new computer. How do I transfer my 43 | Cronometer data to the new computer?

For MS Windows, the 44 | default profile for Cronometer is located in a folder called 45 | "cronometer" at: 46 | 47 |
48 | C:\Documents and Settings\Owner\Application 49 | Data\cronometer 50 |
Where "Owner" is your logon ID name, e.g., "John 51 | Smith". 52 | 53 |

The "Application Data" folder is a system folder that will not 54 | be visible unless you specify that hidden files and folders 55 | should be displayed. You can do this from the "Tools" function in 56 | the menu bar of a window.

57 | 58 |
59 |
60 | Tools->Folder Options->View
61 |   Advanced settings:
62 |    Hidden files and folders:
63 |     o Show hidden files and folders
64 | 
65 |
Just copy the "cronometer" folder from the old 66 | computer to the corresponding location in the new computer, 67 | install Cronometer from the web on your new computer, and you are 68 | set. The process is similar for other operating systems. 69 | 70 | 71 | -------------------------------------------------------------------------------- /docs/about.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

About Cronometer

17 | 18 |

Contributors

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 |
Aaron Davidsonaaron@spaz.caProgramming (Lead)
Chris Rosechris@offlineblog.comProgramming
Ryan DortmanstvstaticProgramming
Gerald Turnquistgeraldt@sasktel.netProgramming
Simon Wernersimon.werner@gmail.comProgramming
Michael Raemikalra@cadvision.comConsulting
Antonio 72 | Zamorazamora@scientificpsychic.comTechnical Writing
79 | 80 |

Food Datasources

USDA 82 | National Nutrient Database for Standard Reference - Release 83 | 24 (USDA sr24) 84 | 85 |

Open Source Libraries

86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 118 | 119 | 120 | 121 | 122 | 123 | 125 | 126 | 127 | 128 | 130 | 131 |
LibraryURLVersionLicense
JFreeCharthttp://jfree.org/1.0.6LGPL
JCommonhttp://jfree.org/1.0.10LGPL
SwingXhttp://swinglabs.org/1.6.2LGPL
132 | 133 | 134 | -------------------------------------------------------------------------------- /docs/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/add.png -------------------------------------------------------------------------------- /docs/addfood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/addfood.png -------------------------------------------------------------------------------- /docs/addserving.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/addserving.png -------------------------------------------------------------------------------- /docs/apple1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/apple1.png -------------------------------------------------------------------------------- /docs/biomarkers.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Biomarkers

Entering Biomarkers
17 | Click on the "Biomarkers" tab for a specific date. Click on the 18 | check box of the specific biomarker that you want to track 19 | (Weight, Body Temperature, Systolic, or Diastolic Blood 20 | Pressure). Enter a numeric value. Note that there are no units 21 | associated with any of these biomarkers. Therefore, you can enter 22 | your weight in pounds or kilograms, and your temperature in 23 | either Celsius or Fahrenheit degrees. Clicking on the 24 | corresponding chart button for each biomarker will display a 25 | graph of the variations over time.
26 |
27 | 28 |
29 | 30
31 |

32 |
33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/biomarkers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/biomarkers.png -------------------------------------------------------------------------------- /docs/blank.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |
17 | Apple 19 | 20 |

Cronometer

A free, open-source, cross-platform, 21 | diet-tracking application. 22 |

23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/calendar.png -------------------------------------------------------------------------------- /docs/copyyesterday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/copyyesterday.png -------------------------------------------------------------------------------- /docs/cron.css: -------------------------------------------------------------------------------- 1 | 2 | body 3 | { 4 | font-family: verdana, arial, serif; 5 | color: #000000; 6 | background: #ffffff; 7 | font-size: 11pt; 8 | margin-top: 10; 9 | margin-bottom: 10px; 10 | margin-left: 20px; 11 | margin-right: 20px; 12 | padding-left: 0; 13 | padding-right: 0; 14 | } 15 | 16 | td { 17 | font-family: verdana, arial, serif; 18 | font-size: 11pt; 19 | } 20 | 21 | th { 22 | text-align: left; 23 | } 24 | 25 | #li { 26 | # text-align: justify 27 | #} 28 | 29 | h1 { 30 | font-size: 18pt; 31 | font-family: verdana, arial, serif; 32 | color: #ffffff; 33 | background: #555555; 34 | padding: 5px; 35 | border: #000 1px solid; 36 | } 37 | 38 | h2 { 39 | font-size: 16pt; 40 | font-family: verdana, arial, serif; 41 | color: #ffffff; 42 | background: #666666; 43 | padding: 4px; 44 | border: #000 1px solid; 45 | } 46 | 47 | h3 { 48 | font-family: verdana, arial, serif; 49 | color: #ffffff; 50 | background: #777777; 51 | padding: 4px; 52 | border: #000 1px solid; 53 | } 54 | 55 | h4 { 56 | font-family: verdana, arial, serif; 57 | color: #ffffff; 58 | background: #888888; 59 | padding: 2px; 60 | border: #000 1px solid; 61 | } 62 | 63 | .va-middle { 64 | vertical-align:middle; 65 | } 66 | 67 | #alert { 68 | background: #ffcccc; 69 | padding: 4px; 70 | margin-left: 40px; 71 | margin-right: 40px; 72 | margin-top: 5px; 73 | margin-bottom: 5px; 74 | font-size: 12pt; 75 | font-style: bold; 76 | text-align: center; 77 | } 78 | -------------------------------------------------------------------------------- /docs/daily.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/daily.png -------------------------------------------------------------------------------- /docs/editpreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/editpreferences.png -------------------------------------------------------------------------------- /docs/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/export.png -------------------------------------------------------------------------------- /docs/food.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Creating a Food

Because of the large number of food 17 | products available to consumers, it is very likely that you use a 18 | food that is not listed in the database. In this case, you will 19 | need to create a new food. You can use the "Food - Create New 20 | Food" function on the Menu Bar, or if there is something similar 21 | in the database, you can export it and then import it, rename it, 22 | and edit it. Basically, you need to specify the name of the food, 23 | the quantity for which you have nutritional data, and then enter 24 | the detailed quantity of nutrients under each of the tabs 25 | below.
26 |
27 | 28 29 | 30 |

Importing a Food

Click on "Food - Import Food" function 31 | to display a file dialog panel. Select the .xml file and 32 | click on the Open button. The imported food will be placed in My 33 | Foods category. 34 | 35 |

Exporting a Food

This function provides a way of sharing 36 | your recipes by creating a file with an .xml extension 37 | that can be sent to another Cronometer user. Click on the food to 38 | select it, and then click the Export Food icon , or right click on the food that you want to 41 | export, and click the "Export Food" icon from the pull-down menu 42 | that appears by the cursor. The dialog box lets you specify the 43 | folder where you would like to save the recipe or food.
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /docs/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/help.png -------------------------------------------------------------------------------- /docs/help.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/limitations.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Limitations

17 | 18 |
19 | The developers of this software accept no liability in the use 20 | of this software. Use at your own risk. It is up to you to 21 | understand the risks and limitations of the software. 22 |
23 | 24 |

Be careful before drawing conclusions from your nutritional 25 | summaries. Always consult your physician before making 26 | significant changes to your diet or supplementation.

27 | 28 |

Nutriton Data

The results displayed are only as good as 29 | the data the program has for the various foods you enter. In many 30 | cases the data may be incomplete, incorrect, or simply different 31 | (was the apple you ate grown in different soil than the ones 32 | studied for the data?). 33 | 34 |

The data displayed in this software should always be treated 35 | as a rough estimate. The data itself may contain errors or be 36 | incomplete. The USDA food database, for instance, contains 37 | average values for most foods. Local soil and growing conditions 38 | can deviate largely from the average values.

39 | 40 |

Underrepresented Nutrients

Some nutrients are 41 | underrepresented in the database because not every food item has 42 | undergone comprehensive analysis. In particular, Fluoride, 43 | Choline, and Vitamin D are significantly lacking data. 44 | 45 |

For instance, values for Vitamin D are missing for many foods 46 | in the USDA nutrition data. Of the 7293 foods listed, only 510 47 | have values for Vitamin D. So if you appear chronically low on 48 | Vitamin D, it may be due to lack of data, not an actual 49 | deficiency in your diet!

50 | 51 |

Software Bugs

This software may contain unknown bugs that 52 | affect the reported values. While we strive to ensure quality, 53 | the possibility of errors remains. 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/menu1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/menu1.png -------------------------------------------------------------------------------- /docs/menubar.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Menu Bar

Menu
18 |
19 | The menu bar has the following functions: 20 | 21 |
    22 |
  • File
    23 |  Manage Users (Ctrl+U) - Add, remove and switch 24 | users.
    25 |  Edit Nutritional Targets (Ctrl+T) - Open a dialog box to 26 | set nutritional targets.
    27 |  Quit (Ctrl+Q) - Save data and terminate program.
    28 |
  • 29 | 30 |
  • Edit - Standard graphical interface editing commands.
    31 |  Cut (Ctrl+X)
    32 |  Copy (Ctrl+C)
    33 |  Copy To - Copy the selected servings to another 34 | user.
    35 |  Paste (Ctrl+V)
    36 |
  • 37 | 38 |
  • Reports
    39 |  Nutrition Report (Ctrl+N) - Produces a report that can be 40 | saved as an HTML or text file.
    41 |
  • 42 | 43 |
  • Food
    44 |  Add Serving (Ctrl+E) - Opens a dialog box to select and 45 | add a food.
    46 |  Import Food (Ctrl+I) - Reads a file of nutritional 47 | information prepared by another Cronometer user.
    48 |  Create New Food (Ctrl+F) - Allows entering nutritional 49 | information for a food that is not in the database.
    50 |  Create New Recipe (Ctrl+R) - Allows using combinations of 51 | foods in the database to create a new food.
    52 |
  • 53 | 54 |
  • Help
    55 |  Help (Ctrl+H) - Display this help file
    56 |  Report a Bug - Opens a web page to report a bug.
    57 |  Request Features - Opens a web page to request new 58 | features.
    59 |  Release Notes - Tells you what is new in the current 60 | release.
    61 |  About - Programming credits
  • 62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/navtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/navtools.png -------------------------------------------------------------------------------- /docs/newfood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/newfood.png -------------------------------------------------------------------------------- /docs/notes.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Notes

17 | 18 |

You may write notes to keep track of your moods, or any other 19 | aspect of your diet or exercise. The notes may help you to record 20 | special procedures for food preparation or the timing of your 21 | meals.


22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/notes.png -------------------------------------------------------------------------------- /docs/nutrient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/nutrient.png -------------------------------------------------------------------------------- /docs/nutrient1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/nutrient1.png -------------------------------------------------------------------------------- /docs/nutrition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/nutrition.png -------------------------------------------------------------------------------- /docs/readme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 |

PLEASE READ CAREFULLY

This tool helps you 11 | track your nutrition. Please understand that the data displayed 12 | in this software should always be treated as a rough estimate, 13 | not the gospel truth. The data itself may contain errors or be 14 | incomplete. The USDA food database, for instance, contains 15 | average values for most foods. Local soil and growing conditions 16 | can deviate largely from the average values. Some nutrients are 17 | underrepresented in the database because not every food item has 18 | undergone comprehensive analysis. Furthermore, this software may 19 | contain unknown bugs that affect the reported values. Thus it is 20 | essential that you do not use this program to make life-critical 21 | decisions.
22 |
23 | 24 |
25 | The developers of this software accept no liability in the 26 | use of this software.
27 | Use at your own risk.
28 |
29 | 30 |

You get what you pay for

This software is provided 31 | free-of-charge, and is the result of countless hours of work by 32 | the developers. We continue to work on this software and support 33 | it at our own leisure. However, if you have an important 34 | 'must-have' feature, we will accept bribes. 35 | 36 |

Help Wanted

This software is made by an all-volunteer 37 | effort. If you want to chip in and lend a hand there are lots of 38 | things you can do to improve the program. Besides programmers, we 39 | could use: 40 | 41 |
    42 |
  • Technical writing skills to make a user-friendly help 43 | manual
  • 44 | 45 |
  • Custom art work for icons and aesthetics
  • 46 | 47 |
  • Scientific accuracy -- help find errors, bugs, or improve 48 | the nutritional analysis
  • 49 |
If you wish to contribute, contact aaron@spaz.ca. 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/recipe.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Creating a Recipe

Creating a Recipe
17 | On the Menu Bar, click on "Food - Create New Recipe". A dialog 18 | box enables you to name the recipe and specify ingredients and 19 | amounts. Click on the "Add Serving" icon .  This causes the "Select a Food" panel to 22 | display. You can now type the name of the first food ingredient 23 | for your recipe. Select the ingredient by clicking on it, enter a 24 | weight, and click the "ADD" button. Repeat the procedure for each 25 | ingredient. In the recipe, you can adjust the amount of each 26 | ingredient by double clicking the amount field and typing a new 27 | value. Similarly, by double clicking the calories, you can type a 28 | new number and the weight will be adjusted accordingly. Foods may 29 | be removed from the recipe by clicking on them and then clicking 30 | on the trash can icon. Click the "Save" button to save the 31 | recipe. 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/release1.0.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | Cronometer Release Notes 11 | 13 | 14 | 15 | 16 |

Cronometer Release Notes

17 | 18 |

What's new in Version 1.2?

19 | 20 |
    21 |
  • Use better icons.
  • 22 | 23 |
  • Indicate the date via color of toolbar.
  • 24 | 25 |
  • Trigger add-item dialog when tables are double 26 | clicked.
  • 27 | 28 |
  • Upgrade to USDA SR26.
  • 29 |
30 | 31 |

What's new in Version 1.1?

32 | 33 |
    34 |
  • Simplify user interface.
  • 35 | 36 |
  • Add OS X Retina display support.
  • 37 |
38 | 39 |

What's new in Version 1.0?

40 | 41 |
    42 |
  • Build on OS X 10.9.
  • 43 | 44 |
  • Remove splash screen.
  • 45 | 46 |
  • Remove system tray icon.
  • 47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/release8.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | Cronometer Release Notes 11 | 13 | 14 | 15 | 16 |

Cronometer Release Notes

17 | 18 |

What's new in Version 0.8?

19 | 20 |
    21 |
  • Food database upgraded to USDA sr19
  • 22 | 23 |
  • Added nutrient data for Choline, Caffeine, and 24 | Fluoride.
  • 25 | 26 |
  • Search results can be sorted by rank or alphabetically by 27 | clicking column headers
  • 28 | 29 |
  • Updated CRDB 30 | 31 |
      32 |
    • Updated nutrition information for CRDB:00006 -- 33 | Brewer's Yeast Lewis Labs
    • 34 | 35 |
    • Added several new foods to CRDB
    • 36 | 37 |
    • Added corrected version of USDA's Almond Butter
    • 38 |
    39 |
  • 40 | 41 |
  • Remembers last window size and position
  • 42 | 43 |
  • System Tray icon for handy access (Requires Java 6 to be 44 | installed)
  • 45 | 46 |
  • Added daily note pad for keeping free-form notes for each 47 | day
  • 48 | 49 |
  • New Help Browser and help content
  • 50 | 51 |
  • Fixed typo: Cholesterol was incorrectly labeled in grams 52 | instead of milligrams.
  • 53 |
54 | 55 |

USDA sr19

The USDA food database has been upgraded to the 56 | most recent release. In addition to adding and updating many 57 | foods with more recent data, Cronometer now adds data for 58 | Choline, Caffeine, and Fluoride. Note that data for Choline and 59 | Fluoride may be under-represented in the data. Some foods that 60 | existed in sr18 have been deleted in sr19. We've kept these foods 61 | for backwards compatibility, but they will show up in the program 62 | greyed-out with accompanying warnings. 63 | 64 |

Updated CRDB

The special CRDB (CR Community Food 65 | Database) has been updated with new data for Brewer's Yeast 66 | Lewis Labs. The USDA's entry for Almond Butter, Unsalted is 67 | missing data for Vitamin E. A corrected entry was added to the 68 | CRDB. We've added several new foods submitted by Michael Rae and 69 | Aaron Davidson. As always, we welcome quality submissions from 70 | our user community for inclusion in the next release. 71 | 72 |

Nutrient Editors

In the food editor, there is now a 73 | distinction made between a nutrient having a known amount of 0 74 | and missing data. Nutrients with no data are shown in grey. 75 | 76 | 77 | -------------------------------------------------------------------------------- /docs/release9.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | Cronometer Release Notes 11 | 13 | 14 | 15 | 16 |

Cronometer Release Notes

17 | 18 |

What's new in Version 0.9.9?

19 | 20 |
    21 |
  • Updated to USDA sr24
  • 22 | 23 |
  • Upgraded to Swingx 1.6.2
  • 24 |
25 | 26 |

What's new in Version 0.9.8?

27 | 28 |
    29 |
  • Updated to USDA sr23
  • 30 | 31 |
  • Update CRDB foods with new submissions from Michael 32 | Rae
  • 33 | 34 |
  • Added data export support for cronometer.com
  • 36 |
37 | 38 |

What's new in Version 0.9.7?

39 | 40 |
    41 |
  • Updated to USDA sr22
  • 42 | 43 |
  • Update CRDB foods with new submissions from Michael 44 | Rae
  • 45 | 46 |
  • Dropping universal binary support for Mac OS X
  • 47 | 48 |
  • Added rudimentary exercise tracker (Patch submitted by Ryan 49 | Dortmans)
  • 50 | 51 |
  • Added multi-file food import (Patch submitted by Ryan 52 | Dortmans)
  • 53 |
54 | 55 |

What's new in Version 0.9.6?

56 | 57 |
    58 |
  • Fixes crash in User Preferences Dialog when using Java 59 | 1.6.0_14.
  • 60 |
61 | 62 |

What's new in Version 0.9.5?

63 | 64 |
    65 |
  • Upgrade food database to USDA sr21.
  • 66 |
67 | 68 |

What's new in Version 0.9.4?

69 | 70 |
    71 |
  • Fixed data corruption bug that could make some users unable 72 | to save or load their history.
  • 73 | 74 |
  • Fixed bug on some systems day comparisons were using system 75 | time (UTC) instead of local time
  • 76 |
77 | 78 |

What's new in Version 0.9.3?

79 | 80 |
    81 |
  • Updated to USDA sr20, which includes many new foods and 82 | more values for Choline.
  • 83 | 84 |
  • Fixed Search Focus Bug in Max OS 10.5 (Leopard)
  • 85 | 86 |
  • Simon Werner contributed code that enables the ability to 87 | track multiple users at once, includes the copying servings 88 | between users and common recipes.
  • 89 |
90 | 91 |

What's new in Version 0.9.0?

92 | 93 |
    94 |
  • Improvements to data entry
  • 95 | 96 |
  • The derived Omega-3 and Omega-6 are now more accurate than 97 | in previous versions.
  • 98 | 99 |
  • Vitamin E's %DV was previously incorrect (was 30 mg instead 100 | of 20.14 mg).
  • 101 | 102 |
  • Other minor bug fixes.
  • 103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /docs/summary1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/summary1.png -------------------------------------------------------------------------------- /docs/targets.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Setting Nutritional Targets

17 | 18 |

When you first run Cronometer you will be asked to set your 19 | nutritional targets for the first time. You can always come back 20 | and change these later. You can either have the program set 21 | default values for your body-type, or manually set them to custom 22 | values.

23 | 24 |

Set your nutritional targets for tracking within the program. 25 | Every nutrient has a target minimum and maximum. The minimum 26 | should be set to the minimum amount you want to consume daily. 27 | The maximum should be set to an amount you never want to exceed. 28 | For instance, the toxicity levels of that nutrient should never 29 | be exceeded. As you approach a maximum target, the progress bars 30 | will go bright red to warn you of the danger.

31 | 32 |

Clicking 'Set to Dietary Reference Intakes' will set all the 33 | targets to values configured for your weight, height, gender, 34 | age, and activity level. These values are based on the DRIs 35 | published by USDA. Please note that DRIs are not specified for 36 | all nutrients, and so many values will remain unchanged. You may 37 | also edit the targets directly in the table to the right choosing 38 | customized values for your tracking.

39 | 40 |

The default nutritional values provided by Cronometer are 41 | based on the official Dietary Reference Intakes (DRI) set by the 42 | USDA. You can change these values to implement any diet. Using 43 | the tabs, navigate to any particular nutrient, double click on 44 | the value that you want to modify, and enter a new value using 45 | the keyboard.

46 | 47 |

To ignore certain nutrients you may not be interested in, 48 | toggle off the tracking checkbox for that nutrient to hide it 49 | within the software. Some nutrients are not tracked by default. 50 | Some values have little to no data available in the nutrition 51 | database, and so a user must understand these limitations before 52 | interpreting tracking data for these nutrients.


53 | 54 |
55 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/targets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/targets.png -------------------------------------------------------------------------------- /docs/tips.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | About Cronometer 11 | 13 | 14 | 15 | 16 |

Tips & Tricks

17 | 18 |

Cut, Copy, and Paste Shortcuts

Standard keyboard 19 | shortcuts for Cut, Copy and Paste will work on selected items in 20 | your daily listing. For example, you can easily copy a selection 21 | of foods and paste them into a different day. You can also paste 22 | the results into a text editor. 23 | 24 |

Keyboard accelerators for food entry

There are also many 25 | ways to accelerate through data entry with the keyboard. 26 | Control-E (Command-E on Max OS X) will bring up the food search 27 | dialog. 28 | 29 |

If the top ranked search result is what you're looking for, 30 | hitting enter will auto-select it. When you select a food, the 31 | keyboard focus automatically transfers to the amount field. 32 | Hitting enter after typing an amount will automatically add the 33 | food. Tab will select the Measure menu and you can use the arrow 34 | keys to page through them. You can actually enter foods without 35 | ever touching the mouse.

36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/today.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/today.png -------------------------------------------------------------------------------- /docs/tomorrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/tomorrow.png -------------------------------------------------------------------------------- /docs/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/toolbar.png -------------------------------------------------------------------------------- /docs/user_manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/user_manager.png -------------------------------------------------------------------------------- /docs/users.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | Managing Users in Cronometer 11 | 13 | 14 | 15 | 16 |

Managing users

17 | 18 |

In Cronometer you can track multiple users diets in the same 19 | session. Cronometer will track your food servings, biomarkers, 20 | notes, dietary targets and body profile separately. It is 21 | possible to copy selected foods from one person to another person 22 | and to a given date. When you add a food for one user, it will be 23 | available for all users in the system. The name of the currently 24 | selected user will be displayed in the title bar of the 25 | Cronometer window. When you exit Cronometer and restart it again 26 | another time, the last selected user will be active.

27 | 28 |

You can find the user manager dialog in the File menu. The 29 | user manager will allow you to add a user or delete a user. To 30 | switch between users click on the user you wish to switch to, 31 | this will immediately update the servings list to reflect the new 32 | user.


33 |
35 | 36 |

Copy servings to another user

37 | 38 |

Select the servings you wish to copy to another user, then 39 | from the "Edit" menu select "Copy To...". You will be asked to 40 | select the user to copy the servings to and the date to which 41 | where the copied servings should be placed.

42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/webversion.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | cronometer.com 11 | 13 | 14 | 15 | 16 |

Cronometer Web Version

The same great easy-to-use 17 | tracking of Cronometer is now available as a web application at 18 | http://cronometer.com/. 19 | 20 |

Why switch to cronometer.com?

21 | 22 |
    23 |
  • Access your food diary from any web browser
  • 24 | 25 |
  • Track on-the-go with the iPhone or Android app
  • 26 | 27 |
  • Easily share your foods, recipes, and progress with 28 | others
  • 29 | 30 |
  • A larger and growing food database
  • 31 | 32 |
  • More detailed charts and graphs
  • 33 | 34 |
  • Track calories burned in exercises
  • 35 |
Once you have created an account at cronometer.com, you can 36 | upload your data to your online account under the Help 37 | Menu -> Export to cronometer.com...
38 |
39 | 40 |
41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/wv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/wv.png -------------------------------------------------------------------------------- /docs/yesterday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/docs/yesterday.png -------------------------------------------------------------------------------- /lib/crdb_005.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/lib/crdb_005.jar -------------------------------------------------------------------------------- /lib/jcommon-1.0.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/lib/jcommon-1.0.10.jar -------------------------------------------------------------------------------- /lib/jfreechart-1.0.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/lib/jfreechart-1.0.6.jar -------------------------------------------------------------------------------- /lib/swingx-core-1.6.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/lib/swingx-core-1.6.2.jar -------------------------------------------------------------------------------- /lib/usda_sr28.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/lib/usda_sr28.jar -------------------------------------------------------------------------------- /release/osx/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleAllowMixedLocalizations 6 | true 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | JavaApplicationStub 11 | CFBundleGetInfoString 12 | 1.1 13 | CFBundleIconFile 14 | cronometer.icns 15 | CFBundleIdentifier 16 | Cronometer 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | Cronometer 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | 1.1 25 | CFBundleVersion 26 | 1.1 27 | CFBundleSignature 28 | kr0n 29 | Java 30 | 31 | Arguments 32 | 33 | ClassPath 34 | $JAVAROOT/cronometer.jar:$JAVAROOT/jcommon-1.0.10.jar:$JAVAROOT/jfreechart-1.0.6.jar:$JAVAROOT/swingx-core-1.6.2.jar:$JAVAROOT/docs.jar:$JAVAROOT/usda_sr28.jar:$JAVAROOT/crdb_005.jar 35 | JVMVersion 36 | 1.5+ 37 | MainClass 38 | ca.spaz.cron.Cronometer 39 | Properties 40 | 41 | apple.laf.useScreenMenuBar 42 | true 43 | 44 | VMOptions 45 | -Xmx128m 46 | WorkingDirectory 47 | $APP_PACKAGE/Contents/Resources/Java 48 | 49 | NSPrincipalClass 50 | NSApplication 51 | 52 | 53 | -------------------------------------------------------------------------------- /release/osx/Contents/MacOS/JavaApplicationStub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/release/osx/Contents/MacOS/JavaApplicationStub -------------------------------------------------------------------------------- /release/osx/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPLP0ki 2 | -------------------------------------------------------------------------------- /release/osx/Contents/Resources/cronometer.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/release/osx/Contents/Resources/cronometer.icns -------------------------------------------------------------------------------- /release/windows/cronometer.exe4j: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /release/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/release/windows/icon.ico -------------------------------------------------------------------------------- /scripts/deprecated.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """List deprecated USDA foods.""" 4 | 5 | import argparse 6 | import sys 7 | 8 | 9 | def read_index(filename): 10 | result = {} 11 | 12 | with open(filename) as input_file: 13 | for line in input_file.readlines(): 14 | (key, value) = line.split('|') 15 | result[key] = value.strip() 16 | 17 | return result 18 | 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument('old', help='old food index') 23 | parser.add_argument('new', help='new food index') 24 | args = parser.parse_args() 25 | 26 | old = read_index(args.old) 27 | new = read_index(args.new) 28 | 29 | for key in set(old) - set(new): 30 | print('{}|{}'.format(key, old[key])) 31 | 32 | 33 | if __name__ == '__main__': 34 | sys.exit(main()) 35 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CopyExercisesAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | import javax.swing.ImageIcon; 7 | 8 | import ca.spaz.cron.exercise.ExerciseTable; 9 | import ca.spaz.cron.foods.ServingTable; 10 | import ca.spaz.util.ImageFactory; 11 | 12 | 13 | public class CopyExercisesAction extends AbstractAction { 14 | private ExerciseTable exerciseTable; 15 | 16 | public CopyExercisesAction(ExerciseTable exerciseTable) { 17 | super("Copy Exercises"); 18 | this.exerciseTable = exerciseTable; 19 | putValue(SHORT_DESCRIPTION, "Copy the selected exercises from this list"); 20 | 21 | } 22 | 23 | public void actionPerformed(ActionEvent e) { 24 | assert (exerciseTable != null); 25 | exerciseTable.copySelectedExercises(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CopyServingsAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.foods.ServingTable; 8 | 9 | 10 | public class CopyServingsAction extends AbstractAction { 11 | private ServingTable servingTable; 12 | 13 | public CopyServingsAction(ServingTable servingTable) { 14 | super("Copy Servings"); 15 | this.servingTable = servingTable; 16 | putValue(SHORT_DESCRIPTION, "Copy the selected servings from this list"); 17 | 18 | } 19 | 20 | public void actionPerformed(ActionEvent e) { 21 | assert (servingTable != null); 22 | servingTable.copySelectedServings(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CopyServingsToUserAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.Cronometer; 8 | import ca.spaz.cron.foods.ServingTable; 9 | import ca.spaz.cron.user.UserDatePickerDialog; 10 | import ca.spaz.cron.user.UserManager; 11 | import ca.spaz.gui.WrapperDialog; 12 | import ca.spaz.util.Logger; 13 | 14 | public class CopyServingsToUserAction extends AbstractAction { 15 | 16 | public CopyServingsToUserAction(ServingTable servingTable) { 17 | super("Copy Servings To..."); 18 | if (UserManager.getUserManager().numberOfUsers() == 1) { 19 | this.setEnabled(false); 20 | } 21 | putValue(SHORT_DESCRIPTION, "Copy the selected servings to another user"); 22 | } 23 | 24 | public void actionPerformed(ActionEvent e) { 25 | copyToUserDialog(); 26 | } 27 | 28 | public static void copyToUserDialog() { 29 | Logger.debug("actionPerformed"); 30 | assert (ServingTable.getServingTable() != null); 31 | UserDatePickerDialog userDP = new UserDatePickerDialog(); 32 | WrapperDialog.showDialog(Cronometer.getInstance(), userDP, true); 33 | 34 | if ( !userDP.cancelPressed()) { 35 | Logger.debug("Date selected is: " + userDP.getDate().toString()); 36 | ServingTable.getServingTable().copySelectedServingsToUser(userDP.getUser(), userDP.getDate()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CreateRecipeAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.Component; 4 | import java.awt.event.ActionEvent; 5 | import java.util.List; 6 | 7 | import javax.swing.AbstractAction; 8 | 9 | import ca.spaz.cron.foods.FoodEditor; 10 | import ca.spaz.cron.foods.Recipe; 11 | 12 | 13 | public class CreateRecipeAction extends AbstractAction { 14 | 15 | private List servings; 16 | private Component parent; 17 | 18 | public CreateRecipeAction(List servings, Component parent) { 19 | super("Create Recipe"); 20 | this.servings = servings; 21 | this.parent = parent; 22 | putValue(SHORT_DESCRIPTION, "Create a new Recipe from the selection"); 23 | } 24 | 25 | public void actionPerformed(ActionEvent e) { 26 | execute(servings); 27 | } 28 | 29 | public static void execute(List servings) { 30 | assert (servings != null); 31 | Recipe r = new Recipe(); 32 | r.addServings(servings); 33 | r.setDescription("New Recipe"); 34 | r.setSourceUID("a" + servings.size() + "-" + r.getCalories()); 35 | FoodEditor.editFood(r); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CutExercisesAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.exercise.ExerciseTable; 8 | 9 | 10 | public class CutExercisesAction extends AbstractAction { 11 | private ExerciseTable exerciseTable; 12 | 13 | public CutExercisesAction(ExerciseTable exerciseTable) { 14 | super("Cut Exercises"); 15 | this.exerciseTable = exerciseTable; 16 | putValue(SHORT_DESCRIPTION, "Cut the selected exercises from this list"); 17 | } 18 | 19 | public void actionPerformed(ActionEvent e) { 20 | assert (exerciseTable != null); 21 | exerciseTable.cutSelectedExercises(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/CutServingsAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.foods.ServingTable; 8 | 9 | 10 | public class CutServingsAction extends AbstractAction { 11 | private ServingTable servingTable; 12 | 13 | public CutServingsAction(ServingTable servingTable) { 14 | super("Cut Servings"); 15 | this.servingTable = servingTable; 16 | putValue(SHORT_DESCRIPTION, "Cut the selected servings from this list"); 17 | } 18 | 19 | public void actionPerformed(ActionEvent e) { 20 | assert (servingTable != null); 21 | servingTable.cutSelectedServings(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/DeleteExercisesAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.exercise.ExerciseTable; 8 | 9 | 10 | public class DeleteExercisesAction extends AbstractAction { 11 | private ExerciseTable exerciseTable; 12 | 13 | public DeleteExercisesAction(ExerciseTable exerciseTable) { 14 | super("Delete Servings"); 15 | this.exerciseTable = exerciseTable; 16 | putValue(SHORT_DESCRIPTION, "Delete the selected servings from this list"); 17 | 18 | } 19 | 20 | public void actionPerformed(ActionEvent e) { 21 | assert (exerciseTable != null); 22 | exerciseTable.deleteSelectedExercise(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/DeleteFoodAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.Component; 4 | import java.awt.event.ActionEvent; 5 | import java.util.List; 6 | 7 | import javax.swing.*; 8 | 9 | import ca.spaz.cron.Cronometer; 10 | import ca.spaz.cron.datasource.FoodProxy; 11 | import ca.spaz.cron.user.UserManager; 12 | 13 | 14 | public class DeleteFoodAction extends AbstractAction { 15 | private FoodProxy food; 16 | private Component parent; 17 | 18 | public DeleteFoodAction(FoodProxy food, Component parent) { 19 | super("Delete Food"); 20 | this.food = food; 21 | this.parent = parent; 22 | putValue(SHORT_DESCRIPTION, "Delete this food from the food database"); 23 | if (!food.getSource().isMutable()) { 24 | setEnabled(false); 25 | } 26 | } 27 | 28 | public void actionPerformed(ActionEvent e) { 29 | doDeleteFood(food, parent); 30 | } 31 | 32 | public static void doDeleteFood(FoodProxy fp, Component parent) { 33 | assert (fp != null); 34 | 35 | List servings = UserManager.getCurrentUser().getFoodHistory().getServings(fp); 36 | int rc = 0; 37 | if (servings.size() == 0) { 38 | rc = JOptionPane.showConfirmDialog(parent, 39 | "Are you sure you want to delete '" + 40 | fp.getDescription() + "'?", 41 | "Delete Food?", JOptionPane.YES_NO_OPTION); 42 | } else { 43 | rc = JOptionPane.showConfirmDialog(parent, 44 | "Are you sure you want to delete '" + 45 | fp.getDescription() + "' and the " + servings.size() + " references to it?", 46 | "Delete Food?", JOptionPane.YES_NO_OPTION); 47 | } 48 | if (rc == JOptionPane.YES_OPTION) { 49 | if (fp.getSource().isMutable()) { 50 | fp.getFood().delete(); 51 | UserManager.getCurrentUser().getFoodHistory().deleteServings(servings); 52 | // TODO: must also delete from recipes that refer to it 53 | Cronometer.getInstance().refreshDisplays(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/DeleteServingsAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.event.ActionEvent; 4 | 5 | import javax.swing.AbstractAction; 6 | 7 | import ca.spaz.cron.foods.ServingTable; 8 | 9 | 10 | public class DeleteServingsAction extends AbstractAction { 11 | private ServingTable servingTable; 12 | 13 | public DeleteServingsAction(ServingTable servingTable) { 14 | super("Delete Servings"); 15 | this.servingTable = servingTable; 16 | putValue(SHORT_DESCRIPTION, "Delete the selected servings from this list"); 17 | 18 | } 19 | 20 | public void actionPerformed(ActionEvent e) { 21 | assert (servingTable != null); 22 | servingTable.deleteSelectedServings(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/EditFoodAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.Component; 4 | import java.awt.event.ActionEvent; 5 | 6 | import javax.swing.AbstractAction; 7 | 8 | import ca.spaz.cron.datasource.FoodProxy; 9 | import ca.spaz.cron.foods.FoodEditor; 10 | 11 | 12 | public class EditFoodAction extends AbstractAction { 13 | 14 | private FoodProxy food; 15 | private Component parent; 16 | 17 | public EditFoodAction(FoodProxy food, Component parent) { 18 | super("Edit Food"); 19 | this.food = food; 20 | this.parent = parent; 21 | putValue(SHORT_DESCRIPTION, "Edit this food"); 22 | if (!food.getSource().isMutable()) { 23 | putValue(SHORT_DESCRIPTION, "Edit a copy of this food"); 24 | putValue(NAME, "Edit a Copy of Food"); 25 | } 26 | } 27 | 28 | public void actionPerformed(ActionEvent e) { 29 | assert (food != null); 30 | FoodEditor.editFood(food.getFood()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/actions/ExportFoodAction.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.actions; 2 | 3 | import java.awt.Component; 4 | import java.awt.event.ActionEvent; 5 | import java.io.*; 6 | 7 | import javax.swing.*; 8 | 9 | import ca.spaz.cron.Cronometer; 10 | import ca.spaz.cron.datasource.FoodProxy; 11 | import ca.spaz.cron.foods.Food; 12 | import ca.spaz.gui.ErrorReporter; 13 | import ca.spaz.util.Logger; 14 | 15 | 16 | public class ExportFoodAction extends AbstractAction { 17 | 18 | private FoodProxy food; 19 | private Component parent; 20 | 21 | public ExportFoodAction(FoodProxy food, Component parent) { 22 | super("Export Food"); 23 | this.food = food; 24 | this.parent = parent; 25 | putValue(SHORT_DESCRIPTION, "Exports this food to a file which can be imported by other users"); 26 | } 27 | 28 | public void actionPerformed(ActionEvent e) { 29 | assert (food != null); 30 | doExportFood(food, parent); 31 | } 32 | 33 | public static void doExportFood(FoodProxy fp, Component parent) { 34 | JFileChooser fd = new JFileChooser(); 35 | fd.setSelectedFile(new File(fp.getDescription() + ".xml")); 36 | if (fd.showSaveDialog(parent) == JFileChooser.APPROVE_OPTION) { 37 | File f = fd.getSelectedFile(); 38 | if (f != null) { 39 | try { 40 | Food food = fp.getFood(); 41 | PrintStream ps = new PrintStream( 42 | new BufferedOutputStream(new FileOutputStream(f))); 43 | food.writeXML(ps, true); 44 | ps.close(); 45 | } catch (IOException ie) { 46 | Logger.error(ie); 47 | ErrorReporter.showError(ie, Cronometer.getInstance()); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/CRDBFoods.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | import java.awt.Color; 4 | 5 | import ca.spaz.cron.foods.Food; 6 | 7 | public class CRDBFoods extends JarXMLFoodDataSource { 8 | 9 | private static final Color CRDB_COL = new Color(0x00, 0x00, 0x70); 10 | 11 | public String getZipFileName() { 12 | return "crdb_005.jar"; 13 | } 14 | 15 | public String getBaseName() { 16 | return "crdb_005"; 17 | } 18 | 19 | public String getName() { 20 | return "CRDB"; 21 | } 22 | 23 | public String toString() { 24 | return getName(); 25 | } 26 | 27 | public boolean isMutable() { 28 | return false; 29 | } 30 | 31 | public void updateFood(Food f) { 32 | } 33 | 34 | public void addFood(Food f) { 35 | 36 | } 37 | 38 | public void removeFood(Food f) { 39 | 40 | } 41 | 42 | public Color getDisplayColor() { 43 | return CRDB_COL; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/Datasources.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | import java.util.*; 4 | 5 | import ca.spaz.cron.user.User; 6 | import ca.spaz.cron.user.UserManager; 7 | import ca.spaz.util.ProgressListener; 8 | 9 | 10 | public class Datasources { 11 | 12 | private static List sources; 13 | 14 | private static UserFoods userDataSource; 15 | private static USDAFoods usdaDataSource; 16 | private static CRDBFoods crdbDataSource; 17 | 18 | public static void initialize(ProgressListener pl) { 19 | sources = new ArrayList(); 20 | if (pl != null) { 21 | pl.progressStart(); 22 | } 23 | userDataSource = new UserFoods(UserManager.getCronometerDirectory()); 24 | userDataSource.initialize(); 25 | sources.add(userDataSource); 26 | if (pl != null) { 27 | pl.progress(25); 28 | } 29 | 30 | crdbDataSource = new CRDBFoods(); 31 | crdbDataSource.initialize(); 32 | sources.add(crdbDataSource); 33 | if (pl != null) { 34 | pl.progress(30); 35 | } 36 | 37 | usdaDataSource = new USDAFoods(); 38 | usdaDataSource.initialize(); 39 | sources.add(usdaDataSource); 40 | if (pl != null) { 41 | pl.progress(90); 42 | pl.progressFinish(); 43 | } 44 | 45 | // jump start lazy inits 46 | User user = UserManager.getCurrentUser(); 47 | if (user != null) { 48 | user.initUserData(); 49 | } 50 | if (pl != null) { 51 | pl.progress(100); 52 | pl.progressFinish(); 53 | } 54 | } 55 | 56 | public static UserFoods getUserFoods() { 57 | return userDataSource; 58 | } 59 | 60 | public static CRDBFoods getCRDBFoods() { 61 | return crdbDataSource; 62 | } 63 | 64 | public static USDAFoods getUSDAFoods() { 65 | return usdaDataSource; 66 | } 67 | 68 | /** 69 | * Retrieve a list of all functional data sources in the application. This list will 70 | * not contain any Datasources that have indicated that they are not available. 71 | * 72 | * @return a List of IFoodDatasource instances consisting of 73 | * only those for which isAvailable() returns true. 74 | */ 75 | public static List getDatasources() { 76 | return sources; 77 | } 78 | 79 | /** 80 | * Closes all data sources in the application. 81 | */ 82 | public static void closeAll() { 83 | for (Iterator iter = sources.iterator(); iter.hasNext(); ) { 84 | FoodDataSource element = (FoodDataSource) iter.next(); 85 | element.close(); 86 | } 87 | // jump start lazy inits 88 | User user = UserManager.getCurrentUser(); 89 | user.saveUserData(); 90 | } 91 | 92 | /** 93 | * Look up a datasource by name 94 | * @param name the name of the datasource to find 95 | * @return the datasource or null if not found 96 | */ 97 | public static FoodDataSource getSource(String name) { 98 | for (Iterator iter = sources.iterator(); iter.hasNext(); ) { 99 | FoodDataSource fds = (FoodDataSource) iter.next(); 100 | if (fds.getName().equals(name)) { 101 | return fds; 102 | } 103 | } 104 | return null; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/DeprecatedFoodProxy.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | /** 4 | * In USDA sr19, they decided to 'delete' many old foods that are 5 | * considered out of date or are no longer sold on the market. 6 | * 7 | * This lets us keep those foods for backwards compatibility, but 8 | * lets the program handle them as deprecated food items. 9 | * 10 | * @author adavidson 11 | */ 12 | public class DeprecatedFoodProxy extends FoodProxy { 13 | 14 | public DeprecatedFoodProxy(String description, FoodDataSource source, String sourceid) { 15 | super(description, source, sourceid); 16 | } 17 | 18 | public boolean isDeprecated() { 19 | return true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/FoodProxy.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | import java.lang.ref.SoftReference; 4 | 5 | import ca.spaz.cron.foods.Food; 6 | 7 | /** 8 | * A lightweight proxy for a Food. Just the food description and the ability 9 | * to obtain the full food object if needed. Used primarily for search results. 10 | * 11 | * @author Aaron Davidson 12 | */ 13 | public class FoodProxy { 14 | private String description; 15 | private String sourceID; 16 | private FoodDataSource source; 17 | private SoftReference food; 18 | private int references = 0; 19 | 20 | public FoodProxy() { 21 | } 22 | 23 | public FoodProxy(Food f) { 24 | description = f.getDescription(); 25 | sourceID = f.getSourceUID(); 26 | source = f.getSource(); 27 | } 28 | 29 | public FoodProxy(String description, FoodDataSource source, String sourceid) { 30 | this.description = description; 31 | this.source = source; 32 | this.sourceID = sourceid; 33 | } 34 | 35 | public boolean equals(FoodProxy fp) { 36 | if (source != fp.getSource()) { 37 | return false; 38 | } 39 | if (!sourceID.equals(fp.getSourceID())) { 40 | return false; 41 | } 42 | if (!description.equals(fp.getDescription())) { 43 | return false; 44 | } 45 | return true; 46 | } 47 | 48 | public void addReference() { 49 | references++; 50 | } 51 | 52 | public int getReferences() { 53 | return references; 54 | } 55 | 56 | public Food getFood() { 57 | Food f = null; 58 | if (food != null) { 59 | f = (Food)food.get(); 60 | } 61 | if (f == null) { 62 | f = source.loadFood(sourceID); 63 | food = new SoftReference(f); 64 | } 65 | return f; 66 | } 67 | 68 | public String getDescription() { 69 | if (description == null) { 70 | description = ""; 71 | } 72 | return description; 73 | } 74 | 75 | public void setDescription(String description) { 76 | this.description = description; 77 | } 78 | 79 | public FoodDataSource getSource() { 80 | return source; 81 | } 82 | 83 | public void setSource(FoodDataSource source) { 84 | this.source = source; 85 | } 86 | 87 | public String getSourceID() { 88 | return sourceID; 89 | } 90 | 91 | public void setSourceID(String sourceID) { 92 | this.sourceID = sourceID; 93 | } 94 | 95 | public boolean isDeprecated() { 96 | return false; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/USDAFoods.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | import java.awt.Color; 4 | 5 | import ca.spaz.cron.foods.Food; 6 | 7 | 8 | public class USDAFoods extends JarXMLFoodDataSource { 9 | 10 | 11 | public String getZipFileName() { 12 | return "usda_sr28.jar"; 13 | } 14 | 15 | public String getBaseName() { 16 | return "usda_sr28"; 17 | } 18 | 19 | public String getName() { 20 | return "USDA"; 21 | } 22 | 23 | public String toString() { 24 | return getName(); 25 | } 26 | 27 | public boolean isMutable() { 28 | return false; 29 | } 30 | 31 | public void updateFood(Food f) { 32 | } 33 | 34 | public void addFood(Food f) { 35 | 36 | } 37 | 38 | public void removeFood(Food f) { 39 | 40 | } 41 | 42 | public Color getDisplayColor() { 43 | return Color.BLACK; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/USDAImport/USDAFood.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource.USDAImport; 2 | 3 | import java.sql.*; 4 | import java.util.Map; 5 | 6 | import ca.spaz.sql.SQLInsert; 7 | import ca.spaz.util.Logger; 8 | 9 | public class USDAFood { 10 | 11 | int ID; 12 | String ndb_id; 13 | String description; 14 | String foodgroup; 15 | double pCF = 4, fCF = 9, cCF = 4; 16 | 17 | 18 | /** 19 | * Construct from USDB Flat file string 20 | * @param str a string from the USDB sr17 flat file 21 | * @param groups map of ids to food groups 22 | */ 23 | public USDAFood(String str, Map groups) { 24 | //~01079~^~0100~^~Milk, reduced fat, fluid, 2% milkfat, with added vitamin A~^~MILK,RED FAT,FLUID,2% MILKFAT,W/ ADDED VIT A~^~~^~~^~Y~^~~^0^~~^^^^ 25 | str = str.replaceAll("\\^\\^\\^\\^", "^~~^~~^~~^~~"); 26 | str = str.replaceAll("\\^\\^", "^~~^"); 27 | str = str.replaceAll("\\^$", "^~~"); 28 | 29 | String[] parts = str.split("\\^"); 30 | for (int i = 0; i < parts.length; i++) { 31 | parts[i] = parts[i].replaceAll("^~", ""); 32 | parts[i] = parts[i].replaceAll("~$", ""); 33 | } 34 | 35 | ndb_id = parts[0]; 36 | foodgroup = (String) groups.get(parts[1]); 37 | description = parts[2]; 38 | if (parts.length == 14) { 39 | try { 40 | pCF = Double.parseDouble(parts[11]); 41 | } catch (NumberFormatException e) {} 42 | try { 43 | fCF = Double.parseDouble(parts[12]); 44 | } catch (NumberFormatException e) {} 45 | try { 46 | cCF = Double.parseDouble(parts[13]); 47 | } catch (NumberFormatException e) {} 48 | } else { 49 | System.out.println("bad parts size?\n\t" + parts.length + "|" + str); 50 | } 51 | } 52 | 53 | public void addToDB(Connection c) { 54 | try { 55 | SQLInsert s = new SQLInsert("Food"); 56 | s.getColumns().add("ID", null); 57 | s.getColumns().add("ndb_id", ndb_id); 58 | s.getColumns().add("description", description); 59 | s.getColumns().add("foodgroup", foodgroup); 60 | s.getColumns().add("source", "USDA sr17"); 61 | s.getColumns().add("sourceUID", "sql.usda-sr17." + ndb_id); 62 | s.execute(c); 63 | 64 | // get auto-incremented ID 65 | ResultSet rs = c.createStatement().executeQuery("CALL IDENTITY()"); 66 | rs.next(); 67 | ID = rs.getInt(1); 68 | } catch (SQLException e) { 69 | Logger.error("parseFood(String)", e); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/USDAImport/USDAWeight.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource.USDAImport; 2 | 3 | import java.util.HashMap; 4 | 5 | import ca.spaz.cron.foods.Food; 6 | import ca.spaz.cron.foods.Measure; 7 | 8 | public class USDAWeight { 9 | 10 | String ndb_id; 11 | double amount; 12 | double grams; 13 | String description; 14 | 15 | 16 | public USDAWeight(String str) { 17 | String[] parts = str.split("\\^"); 18 | for (int i = 0; i < parts.length; i++) { 19 | parts[i] = parts[i].replaceAll("^~", ""); 20 | parts[i] = parts[i].replaceAll("~$", ""); 21 | } 22 | ndb_id = parts[0]; 23 | if (parts[2].length() == 0) { 24 | amount = 1.0; // hack for bad data 25 | } else { 26 | amount = Double.parseDouble(parts[2]); 27 | } 28 | description = parts[3]; 29 | grams = Double.parseDouble(parts[4]); 30 | } 31 | 32 | public void addToDB(HashMap foods) { 33 | Food food = (Food)foods.get(ndb_id); 34 | assert (food != null); 35 | food.getMeasures().add(new Measure(amount, description, grams)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/datasource/UserFoodsListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.datasource; 2 | 3 | public interface UserFoodsListener { 4 | 5 | public void userFoodAdded(FoodProxy fp); 6 | public void userFoodModified(FoodProxy fp); 7 | public void userFoodDeleted(FoodProxy fp); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/exercise/ExerciseDialog.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.exercise; 2 | 3 | import java.awt.*; 4 | import java.awt.event.*; 5 | 6 | import javax.swing.*; 7 | 8 | import ca.spaz.util.ToolBox; 9 | 10 | public class ExerciseDialog extends JDialog implements ExerciseEditorListener { 11 | 12 | private ExerciseEditor exerciseEditor; 13 | private JPanel mainPanel; 14 | private boolean abort = true; 15 | private Exercise exercise = null; 16 | 17 | public ExerciseDialog(Frame parent) { 18 | super(parent); 19 | init(parent); 20 | } 21 | 22 | public ExerciseDialog(Dialog parent) { 23 | super(parent); 24 | init(parent); 25 | } 26 | 27 | private void init(Window parent) { 28 | this.setTitle("Add exercise"); 29 | this.getContentPane().add(getMainPanel()); 30 | this.pack(); 31 | ToolBox.centerOver(this, parent); 32 | this.setModal(true); 33 | 34 | // Add escape listener to dismiss window. 35 | getRootPane().registerKeyboardAction( 36 | new ActionListener() { 37 | public void actionPerformed(ActionEvent e) { 38 | dispose(); 39 | } 40 | }, 41 | KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 42 | JComponent.WHEN_IN_FOCUSED_WINDOW); 43 | } 44 | 45 | public void display(boolean addable) { 46 | getExerciseEditor().getAddButton().setVisible(addable); 47 | this.setVisible(true); 48 | } 49 | 50 | public Exercise getSelectedExercise() { 51 | if (abort) { 52 | return null; 53 | } 54 | return exercise; 55 | } 56 | 57 | public JPanel getMainPanel() { 58 | if (null == mainPanel) { 59 | mainPanel = new JPanel(new BorderLayout(4, 4)); 60 | mainPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 61 | mainPanel.add(getExerciseEditor(), BorderLayout.CENTER); 62 | } 63 | return mainPanel; 64 | } 65 | 66 | public ExerciseEditor getExerciseEditor() { 67 | if (exerciseEditor == null) { 68 | exerciseEditor = new ExerciseEditor(); 69 | exerciseEditor.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); 70 | exerciseEditor.addExerciseEditorListener(this); 71 | } 72 | return exerciseEditor; 73 | } 74 | 75 | public void exerciseChosen(Exercise s) { 76 | exercise = s; 77 | abort = false; 78 | dispose(); 79 | } 80 | 81 | public void dispose() { 82 | super.dispose(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/exercise/ExerciseEditorListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.exercise; 2 | 3 | public interface ExerciseEditorListener { 4 | public void exerciseChosen(Exercise e); 5 | } 6 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/exercise/ExerciseHistory.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.exercise; 2 | 3 | import java.util.*; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.cron.records.History; 8 | import ca.spaz.cron.records.Record; 9 | 10 | /** 11 | * The Exercise history for a user. 12 | */ 13 | public class ExerciseHistory extends History { 14 | 15 | public String getBaseName() { 16 | return "exercises"; 17 | } 18 | 19 | public String getEntryTagName() { 20 | return "exercise"; 21 | } 22 | 23 | public Record loadUserEntry(Element item) { 24 | return new Exercise(item); 25 | } 26 | 27 | /** 28 | * Add a new record of a Serving to the history 29 | */ 30 | public synchronized void addExercise(Exercise c) { 31 | addEntry(c); 32 | } 33 | 34 | public synchronized List getConsumedOn(Date curDate) { 35 | return getEntriesOn(curDate); 36 | } 37 | 38 | /** 39 | * Copies the servings from one day to another. 40 | * @param fromDate the day to copy from 41 | * @param toDate the day to copy to 42 | * @return the copied servings 43 | */ 44 | public synchronized List copyExercisesOn(Date fromDate, Date toDate) { 45 | List prevConsumed = getConsumedOn(fromDate); 46 | List consumed = new ArrayList(); 47 | Iterator iter = prevConsumed.iterator(); 48 | while (iter.hasNext()) { 49 | Exercise exercise = new Exercise((Exercise) iter.next()); 50 | exercise.setDate(toDate); 51 | addExercise(exercise); 52 | } 53 | return consumed; 54 | } 55 | 56 | public synchronized void deleteExercises(List list) { 57 | super.deleteEntries(list); 58 | } 59 | 60 | public synchronized void delete(Exercise exercise) { 61 | super.deleteEntry(exercise); 62 | } 63 | 64 | public void update(Exercise exercise) { 65 | super.updateEntry(exercise); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/exercise/ExercisePanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.exercise; 2 | 3 | import java.awt.BorderLayout; 4 | 5 | import javax.swing.BorderFactory; 6 | import javax.swing.JPanel; 7 | 8 | import ca.spaz.cron.Cronometer; 9 | import ca.spaz.cron.ui.DailySummary; 10 | 11 | public class ExercisePanel extends JPanel { 12 | 13 | private ExerciseTable exerciseTable; 14 | 15 | public ExercisePanel() { 16 | setLayout(new BorderLayout(4, 4)); 17 | setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 18 | add(getExerciseTable(), BorderLayout.CENTER); 19 | } 20 | 21 | public ExerciseTable getExerciseTable() { 22 | if (null == exerciseTable) { 23 | exerciseTable = ExerciseTable.getExerciseTable(); 24 | 25 | exerciseTable.addExerciseEditorListener(new ExerciseEditorListener() { 26 | public void exerciseChosen(Exercise e) { 27 | DailySummary ds = Cronometer.getDailySummary(); 28 | if (ds.isOkToAddServings(ds.getDate(), false)) { 29 | ds.addExercise(e); 30 | } 31 | } 32 | }); 33 | } 34 | return exerciseTable; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/exercise/ExerciseSelection.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.exercise; 2 | 3 | import java.awt.datatransfer.*; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | 8 | public class ExerciseSelection implements Transferable { 9 | 10 | public static final DataFlavor exerciseFlavor = new DataFlavor(Exercise[].class, "Exercise"); 11 | public static final DataFlavor[] flavors = {exerciseFlavor}; 12 | 13 | private Exercise[] exercises; 14 | 15 | public ExerciseSelection(Exercise[] list) { 16 | exercises = list; 17 | } 18 | 19 | public ExerciseSelection(ExerciseTable table) { 20 | List sel = table.getSelectedExercises(); 21 | exercises = new Exercise[sel.size()]; 22 | sel.toArray(exercises); 23 | } 24 | 25 | public DataFlavor[] getTransferDataFlavors() { 26 | return flavors; 27 | } 28 | 29 | public boolean isDataFlavorSupported(DataFlavor flavor) { 30 | return flavor.equals(exerciseFlavor); 31 | } 32 | 33 | public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 34 | if (flavor.equals(exerciseFlavor)) { 35 | return (exercises); 36 | } else { 37 | throw new UnsupportedFlavorException(flavor); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/FoodHistory.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | import java.util.*; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.cron.datasource.FoodProxy; 8 | import ca.spaz.cron.records.History; 9 | import ca.spaz.cron.records.Record; 10 | 11 | /** 12 | * The Serving history for a user. 13 | * 14 | * @author Aaron Davidson 15 | */ 16 | public class FoodHistory extends History { 17 | 18 | public String getBaseName() { 19 | return "servings"; 20 | } 21 | 22 | public String getEntryTagName() { 23 | return "serving"; 24 | } 25 | 26 | public Record loadUserEntry(Element item) { 27 | return new Serving(item); 28 | } 29 | 30 | /** 31 | * Add a new record of a Serving to the history 32 | */ 33 | public synchronized void addServing(Serving c) { 34 | addEntry(c); 35 | c.getFoodProxy().addReference(); 36 | } 37 | 38 | public synchronized List getConsumedOn(Date curDate) { 39 | return getEntriesOn(curDate); 40 | } 41 | 42 | /** 43 | * Copies the servings from one day to another. 44 | * @param fromDate the day to copy from 45 | * @param toDate the day to copy to 46 | * @return the copied servings 47 | */ 48 | public synchronized List copyConsumedOn(Date fromDate, Date toDate) { 49 | List prevConsumed = getConsumedOn(fromDate); 50 | List consumed = new ArrayList(); 51 | Iterator iter = prevConsumed.iterator(); 52 | while (iter.hasNext()) { 53 | Serving serving = new Serving((Serving) iter.next()); 54 | serving.setDate(toDate); 55 | addServing(serving); 56 | } 57 | return consumed; 58 | } 59 | 60 | public synchronized List getServings(FoodProxy fp) { 61 | ArrayList res = new ArrayList(); 62 | for (int i = 0; i < entries.size(); i++) { 63 | Serving s = (Serving)entries.get(i); 64 | if (s.getFoodProxy().equals(fp)) { 65 | res.add(s); 66 | } 67 | } 68 | return res; 69 | } 70 | 71 | public synchronized void deleteServings(List list) { 72 | super.deleteEntries(list); 73 | } 74 | 75 | public synchronized void delete(Serving serving) { 76 | super.deleteEntry(serving); 77 | } 78 | 79 | public void update(Serving serving) { 80 | super.updateEntry(serving); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/FoodSelectionListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | import ca.spaz.cron.datasource.FoodProxy; 4 | 5 | public interface FoodSelectionListener { 6 | public void foodSelected(FoodProxy food); 7 | public void foodDoubleClicked(FoodProxy food); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/NutrientEditorTable.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.util.List; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.TableColumn; 9 | import javax.swing.table.TableColumnModel; 10 | 11 | import ca.spaz.gui.PrettyTable; 12 | 13 | public class NutrientEditorTable extends JScrollPane { 14 | 15 | NutrientTableModel model; 16 | PrettyTable nutrientTable; 17 | 18 | public NutrientEditorTable(List nutrients) { 19 | model = new NutrientTableModel(nutrients); 20 | setViewportView(getNutrientTable()); 21 | getViewport().setBackground(Color.WHITE); 22 | setPreferredSize(new Dimension(350, 185)); 23 | } 24 | 25 | public void setMultiplier(double val) { 26 | model.setMultiplier(val); 27 | } 28 | 29 | public void setFood(Food f) { 30 | model.setFood(f); 31 | } 32 | 33 | private JTable getNutrientTable() { 34 | if (null == nutrientTable) { 35 | nutrientTable = new PrettyTable(model); 36 | nutrientTable.getSelectionModel().setSelectionMode( 37 | ListSelectionModel.SINGLE_SELECTION); 38 | nutrientTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 39 | nutrientTable.getTableHeader().setReorderingAllowed(false); 40 | // right align last column 41 | TableColumnModel tcm = nutrientTable.getColumnModel(); 42 | TableColumn column = tcm.getColumn(0); 43 | column.setMinWidth(140); 44 | } 45 | return nutrientTable; 46 | } 47 | 48 | public void setEditable(boolean val) { 49 | nutrientTable.setEnabled(val); 50 | } 51 | 52 | public boolean isEditing() { 53 | return nutrientTable.isEditing(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/NutrientTable.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * A base class for a group of related nutrient values Provides common support 7 | * for loading and updating the database. 8 | * 9 | * The nutrient table is expected to have field names that match the names and 10 | * types of the corresponding database table. This greatly simplifies 11 | * maintaining the database and generating UI forms through reflection. 12 | * 13 | * @author davidson 14 | */ 15 | public class NutrientTable { 16 | 17 | public double[] nutrients = new double[NutrientInfo.getGlobalList().size()]; 18 | 19 | public NutrientTable() { 20 | Arrays.fill(nutrients, -1); 21 | } 22 | 23 | public NutrientTable(NutrientTable nt) { 24 | System.arraycopy(nt.nutrients, 0, nutrients, 0, nutrients.length); 25 | } 26 | 27 | public double getAmount(int index) { 28 | double val = nutrients[index]; 29 | if (val < 0) { 30 | val = 0; 31 | } 32 | return val; 33 | } 34 | 35 | public boolean dataExists(int index) { 36 | return nutrients[index] >= 0; 37 | } 38 | 39 | public void setAmount(int index, double val) { 40 | nutrients[index] = val; 41 | } 42 | 43 | /** 44 | * Add the nutrients in the given table to our total 45 | * 46 | * @param toAdd 47 | * the nutrients ratios to add 48 | * @param weight 49 | * multiplier for the amount in the added nutrients 50 | */ 51 | public void addFood(NutrientTable toAdd, double weight) { 52 | for (int i = 0; i < nutrients.length; i++) { 53 | nutrients[i] += toAdd.getAmount(i) * weight; 54 | } 55 | } 56 | 57 | public void addFood(Serving food) { 58 | addFood(food.getFood().getNutrients(), food.getGrams() / 100.0); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/ServingEditorListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | 4 | public interface ServingEditorListener { 5 | public void servingChosen(Serving s); 6 | } 7 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/ServingSelection.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | import java.awt.datatransfer.*; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | 8 | public class ServingSelection implements Transferable { 9 | 10 | public static final DataFlavor servingFlavor = new DataFlavor(Serving[].class, "Serving"); 11 | public static final DataFlavor[] flavors = {servingFlavor}; 12 | 13 | private Serving[] servings; 14 | 15 | public ServingSelection(Serving[] list) { 16 | servings = list; 17 | } 18 | 19 | public ServingSelection(ServingTable table) { 20 | List sel = table.getSelectedServings(); 21 | servings = new Serving[sel.size()]; 22 | sel.toArray(servings); 23 | } 24 | 25 | public DataFlavor[] getTransferDataFlavors() { 26 | return flavors; 27 | } 28 | 29 | public boolean isDataFlavorSupported(DataFlavor flavor) { 30 | return flavor.equals(servingFlavor); 31 | } 32 | 33 | public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 34 | if (flavor.equals(servingFlavor)) { 35 | return (servings); 36 | } else { 37 | throw new UnsupportedFlavorException(flavor); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/foods/ServingSelectionListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.foods; 2 | 3 | 4 | public interface ServingSelectionListener { 5 | public void servingSelected(Serving s); 6 | public void servingDoubleClicked(Serving s); 7 | public void servingChosen(Serving s); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/BiomarkerPanelOld.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | import java.awt.*; 4 | import java.util.*; 5 | import java.util.List; 6 | 7 | import javax.swing.BorderFactory; 8 | import javax.swing.JPanel; 9 | 10 | import ca.spaz.cron.user.UserManager; 11 | 12 | 13 | /** This old version is still being used. 14 | * 15 | * The new version is not being used yet, though there are getters that 16 | * reference it. 17 | */ 18 | public class BiomarkerPanelOld extends JPanel { 19 | private Date curDate = new Date(); 20 | private List curMetrics; 21 | private MetricEditorOld[] editors; 22 | private List biomarkers = new ArrayList(); 23 | 24 | public BiomarkerPanelOld() { 25 | 26 | biomarkers = UserManager.getCurrentUser().getBiomarkerDefinitions().getEnabledBiomarkers(); 27 | // Create an editor for each enabled biomarker 28 | editors = new MetricEditorOld[biomarkers.size()]; 29 | for (int i = 0; i < editors.length; i++) { 30 | Biomarker biomarker = (Biomarker)biomarkers.get(i); 31 | editors[i] = new MetricEditorOld(this, biomarker); 32 | } 33 | 34 | JPanel fp = new JPanel(new GridLayout(4, 4)); 35 | fp.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 36 | // Note: the grid bag layout is all done here rather than in Metric Editor so that the 37 | // column alignment works properly across the entire grid. 38 | JPanel ed = new JPanel(new GridBagLayout()); 39 | GridBagConstraints c = new GridBagConstraints(); 40 | c.fill = GridBagConstraints.NONE; 41 | c.weightx = 0.0; 42 | c.insets = new Insets(8, 8, 8, 8); 43 | for (int i = 0; i < editors.length; i++) { 44 | c.gridx = 0; 45 | c.gridy = i; 46 | ed.add(editors[i].getLabel(), c); 47 | c.gridx = 1; 48 | c.gridy = i; 49 | ed.add(editors[i].getEntryField(), c); 50 | c.gridx = 2; 51 | c.gridy = i; 52 | ed.add(editors[i].getSaveButton(), c); 53 | c.gridx = 3; 54 | c.gridy = i; 55 | ed.add(editors[i].getDeleteButton(), c); 56 | c.gridx = 4; 57 | c.gridy = i; 58 | ed.add(editors[i].getPlotButton(), c); 59 | } 60 | JPanel x = new JPanel(new BorderLayout()); 61 | x.add(ed, BorderLayout.NORTH); 62 | setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 63 | setLayout(new BorderLayout(4, 4)); 64 | add(x, BorderLayout.WEST); 65 | 66 | // Avoid ugly color mismatch with background. 67 | fp.setOpaque(false); 68 | ed.setOpaque(false); 69 | x.setOpaque(false); 70 | } 71 | 72 | public void setDate(Date d) { 73 | this.curDate = d; 74 | curMetrics = null; 75 | for (int i = 0; i < editors.length; i++) { 76 | editors[i].setMetrics(getMetrics()); 77 | } 78 | } 79 | 80 | public Date getDate() { 81 | return curDate; 82 | } 83 | 84 | private List getMetrics() { 85 | if (curMetrics == null) { 86 | curMetrics = UserManager.getCurrentUser().getBiometrics(curDate); 87 | } 88 | return curMetrics; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/BiometricsHistory.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | import java.util.*; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.cron.records.History; 8 | import ca.spaz.cron.records.Record; 9 | 10 | /** 11 | * The Biometrics history for a user. 12 | * 13 | * @author Aaron Davidson 14 | */ 15 | public class BiometricsHistory extends History { 16 | 17 | public BiometricsHistory() { 18 | super(); 19 | } 20 | 21 | public String getBaseName() { 22 | return "metrics"; 23 | } 24 | 25 | public String getEntryTagName() { 26 | return "metric"; 27 | } 28 | 29 | public Record loadUserEntry(Element item) { 30 | return new Metric(item); 31 | } 32 | 33 | /** 34 | * Add a new record of a Metric to the history 35 | */ 36 | public synchronized void addMetric(Metric m) { 37 | addEntry(m); 38 | } 39 | 40 | public synchronized List getMetricsOn(Date curDate) { 41 | return getEntriesOn(curDate); 42 | } 43 | 44 | public List getMetricsOfType(String type) { 45 | ArrayList res = new ArrayList(); 46 | for (int i = 0; i < entries.size(); i++) { 47 | Metric m = (Metric)entries.get(i); 48 | if (m.getName().equals(type)) { 49 | res.add(m); 50 | } 51 | } 52 | return res; 53 | } 54 | 55 | public void delete(Metric m) { 56 | deleteEntry(m); 57 | } 58 | 59 | public void update(Metric m) { 60 | updateEntry(m); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/Metric.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | import java.util.Date; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.cron.records.Record; 8 | import ca.spaz.util.XMLNode; 9 | 10 | public class Metric implements Comparable, Record { 11 | public static String WEIGHT_UNIT = null; 12 | 13 | private String name; 14 | private Number value; 15 | private Date date; 16 | 17 | public Metric() { 18 | setDate(new Date()); 19 | } 20 | 21 | public Metric(String name, Date d) { 22 | setName(name); 23 | setDate(d); 24 | } 25 | 26 | public Metric(Biomarker biomarker) { 27 | setName(biomarker.getName()); 28 | } 29 | 30 | public Metric(Element e) { 31 | load(e); 32 | } 33 | 34 | public void load(Element e) { 35 | setName(e.getAttribute("name")); 36 | setValue(e.getAttribute("value")); 37 | setDate(new Date(Long.parseLong(e.getAttribute("date")))); 38 | } 39 | 40 | /** 41 | * Compares two metrics by date for sorting. 42 | */ 43 | public int compareTo(Object object) { 44 | return date.compareTo(((Metric)object).getDate()); 45 | } 46 | 47 | public Date getDate() { 48 | return date; 49 | } 50 | 51 | public void setDate(Date date) { 52 | this.date = date; 53 | } 54 | 55 | public String getName() { 56 | return name; 57 | } 58 | 59 | public void setName(String name) { 60 | this.name = name; 61 | } 62 | 63 | public Number getValue() { 64 | return value; 65 | } 66 | 67 | public void setValue(String value) { 68 | this.value = new Double(value); 69 | } 70 | 71 | public void setValue(Number value) { 72 | this.value = value; 73 | } 74 | 75 | public XMLNode toXML() { 76 | XMLNode node = new XMLNode("metric"); 77 | node.addAttribute("name", getName()); 78 | node.addAttribute("date", getDate().getTime()); 79 | node.addAttribute("value", getValue().doubleValue()); 80 | if (name.equals("Weight") && WEIGHT_UNIT != null) { 81 | node.addAttribute("unit", WEIGHT_UNIT); 82 | } 83 | return node; 84 | } 85 | 86 | public String toString() { 87 | return getName() + "-" + getDate() + "-" + getValue(); 88 | } 89 | 90 | public Record copy() { 91 | Metric m = new Metric(); 92 | m.setDate(date); 93 | m.setName(name); 94 | m.setValue(value); 95 | return m; 96 | } 97 | 98 | public boolean isLoaded() { 99 | return true; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/MetricEditorListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | 4 | public interface MetricEditorListener { 5 | public void metricSelected(Metric metric); 6 | public void metricDoubleClicked(Metric metric); 7 | public void metricChosen(Metric metric); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/MetricSelectionListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | 4 | public interface MetricSelectionListener { 5 | public void metricSelected(Metric metric); 6 | public void metricDoubleClicked(Metric metric); 7 | public void metricChosen(Metric metric); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/MetricTable.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.util.List; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.DefaultTableCellRenderer; 9 | import javax.swing.table.TableColumnModel; 10 | 11 | import ca.spaz.cron.Cronometer; 12 | import ca.spaz.cron.records.RecordTable; 13 | import ca.spaz.cron.user.UserManager; 14 | 15 | public class MetricTable extends RecordTable { 16 | 17 | public MetricTable() { 18 | super(new MetricTableModel()); 19 | setTitle("BioMarkers"); 20 | } 21 | 22 | public void doAddNewEntry() { 23 | AddMetricDialog md = new AddMetricDialog(JOptionPane.getFrameForComponent(this)); 24 | md.display(true); 25 | md.getListPanel().requestFocus(); 26 | 27 | Metric metric = md.getSelectedMetric(); 28 | if (metric != null) { 29 | metric.setDate(Cronometer.getInstance().getDailySummary().getDate()); 30 | addMetric(metric); 31 | fireUserEntryChosen(metric); 32 | } 33 | } 34 | 35 | private JComponent makeJScrollPane() { 36 | JScrollPane jsp = new JScrollPane(getTable()); 37 | jsp.setPreferredSize(new Dimension(400, 250)); 38 | jsp.getViewport().setBackground(Color.WHITE); 39 | jsp.setBorder(BorderFactory.createEtchedBorder()); 40 | return jsp; 41 | } 42 | 43 | protected void initTable() { 44 | table.setColumnSelectionAllowed(false); 45 | table.getSelectionModel().setSelectionMode( 46 | ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 47 | table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 48 | table.getTableHeader().setReorderingAllowed(false); 49 | 50 | table.getColumnModel().getColumn(MetricTableModel.TIME_COL).setMinWidth(60); 51 | table.getColumnModel().getColumn(MetricTableModel.BIOMARKER_COL).setMinWidth(100); 52 | table.getColumnModel().getColumn(MetricTableModel.VALUE_COL).setMaxWidth(60); 53 | table.getColumnModel().getColumn(MetricTableModel.UNITS_COL).setMinWidth(60); 54 | 55 | TableColumnModel tcm = table.getColumnModel(); 56 | // Center the time column 57 | DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); 58 | renderer.setHorizontalAlignment(SwingConstants.CENTER); 59 | tcm.getColumn(MetricTableModel.TIME_COL).setCellRenderer(renderer); 60 | // Right align value column 61 | renderer = new DefaultTableCellRenderer(); 62 | renderer.setHorizontalAlignment(SwingConstants.RIGHT); 63 | tcm.getColumn(MetricTableModel.VALUE_COL).setCellRenderer(renderer); 64 | } 65 | 66 | public void setMetrics(List metrics) { 67 | getMetricTableModel().setMetrics(metrics); 68 | fireStateChangedEvent(); 69 | } 70 | 71 | public List getMetrics() { 72 | return getMetricTableModel().getMetrics(); 73 | } 74 | 75 | public void addMetric(Metric metric) { 76 | model.addUserEntry(metric); 77 | UserManager.getCurrentUser().getBiometricsHistory().addMetric(metric); 78 | fireStateChangedEvent(); 79 | } 80 | 81 | public MetricTableModel getMetricTableModel() { 82 | return (MetricTableModel)model; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/metrics/MetricTableModel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.metrics; 2 | 3 | import java.text.*; 4 | import java.util.List; 5 | 6 | import ca.spaz.cron.user.UserManager; 7 | import ca.spaz.cron.records.RecordTableModel; 8 | 9 | /** 10 | * MetricTableModel is the model for displaying a table of biomarker measurements. 11 | */ 12 | public class MetricTableModel extends RecordTableModel { 13 | public static final int TIME_COL = 0; 14 | public static final int BIOMARKER_COL = 1; 15 | public static final int VALUE_COL = 2; 16 | public static final int UNITS_COL = 3; 17 | 18 | public static String[] columnNames = { "Time", "Biomarker", "Value", "Units" }; 19 | 20 | private static final DecimalFormat kcalf = new DecimalFormat("######0.0"); 21 | private static final DecimalFormat amountf = new DecimalFormat("######0.##"); 22 | private static final DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); 23 | 24 | public void setMetrics(List list) { 25 | setUserEntrys(list); 26 | } 27 | 28 | public List getMetrics() { 29 | return getUserEntrys(); 30 | } 31 | 32 | public Class getColumnClass(int col) { 33 | Object o = getValueAt(0, col); 34 | if (o != null) { 35 | return o.getClass(); 36 | } 37 | return String.class; 38 | } 39 | 40 | public int getColumnCount() { 41 | return columnNames.length; 42 | } 43 | 44 | public String getColumnName(int col) { 45 | return columnNames[col].toString(); 46 | } 47 | 48 | public Metric getMetric(int i) { 49 | return (Metric)getUserEntry(i); 50 | } 51 | 52 | public int getNumMetrics() { 53 | return getUserEntrys().size(); 54 | } 55 | 56 | public int getRowCount() { 57 | return getUserEntrys().size(); 58 | } 59 | 60 | public Object getValueAt(int row, int col) { 61 | Metric metric = getMetric(row); 62 | if (metric != null) { 63 | switch (col) { 64 | case TIME_COL: 65 | return timeFormat.format(metric.getDate()); 66 | case UNITS_COL: 67 | return (UserManager.getCurrentUser().getBiomarkerDefinitions().getBiomarker(metric.getName()).getUnits()); 68 | case BIOMARKER_COL: 69 | return metric.getName(); 70 | case VALUE_COL: 71 | return metric.getValue(); 72 | } 73 | } 74 | return ""; 75 | } 76 | 77 | public boolean isCellEditable(int row, int col) { 78 | if (col == VALUE_COL) { 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | public void setValueAt(Object value, int row, int col) { 85 | if (row < 0 || row >= getRowCount()) { 86 | return; 87 | } 88 | Metric metric = getMetric(row); 89 | if (value == null || metric == null) { 90 | return; 91 | } 92 | if (col == VALUE_COL) { 93 | try { 94 | metric.setValue((Double)value); 95 | } catch (NumberFormatException e) {} 96 | } 97 | fireTableRowsUpdated(row, row); 98 | } 99 | 100 | public String getToolTipText(int r, int c) { 101 | return null; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/notes/Note.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.notes; 2 | 3 | import java.util.Date; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.cron.records.Record; 8 | import ca.spaz.util.XMLNode; 9 | 10 | public class Note implements Record { 11 | private long time = System.currentTimeMillis(); 12 | private String note; 13 | 14 | public Record copy() { 15 | Note n = new Note(); 16 | n.setTime(time); 17 | setNote(note); 18 | return n; 19 | } 20 | 21 | public String getNote() { 22 | return note; 23 | } 24 | 25 | public void setDate(Date d) { 26 | time = d.getTime(); 27 | } 28 | 29 | public void setTime(long t) { 30 | time = t; 31 | } 32 | 33 | public Date getDate() { 34 | return new Date(time); 35 | } 36 | 37 | public void load(Element e) { 38 | this.time = XMLNode.getLong(e, "time"); 39 | this.note = XMLNode.getTextContent(e); 40 | } 41 | 42 | public XMLNode toXML() { 43 | XMLNode node = new XMLNode("note"); 44 | node.addAttribute("time", time); 45 | if (note != null) { 46 | node.setText(note); 47 | } 48 | return node; 49 | } 50 | 51 | public void setNote(String n) { 52 | this.note = n; 53 | } 54 | 55 | public boolean isLoaded() { 56 | return true; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/notes/NoteEditor.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.notes; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.awt.event.FocusEvent; 6 | import java.awt.event.FocusListener; 7 | import java.util.Date; 8 | 9 | import javax.swing.*; 10 | import javax.swing.text.BadLocationException; 11 | 12 | import ca.spaz.cron.user.UserManager; 13 | 14 | public class NoteEditor extends JPanel implements FocusListener { 15 | 16 | private JScrollPane jsp; 17 | private JTextArea edit; 18 | private Date curDate = null; 19 | 20 | public NoteEditor() { 21 | setLayout(new BorderLayout(4, 4)); 22 | setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); 23 | jsp = new JScrollPane(getEditor()); 24 | jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); 25 | jsp.setPreferredSize(new Dimension(200, 200)); 26 | add(jsp, BorderLayout.CENTER); 27 | } 28 | 29 | private JTextArea getEditor() { 30 | if (edit == null) { 31 | edit = new JTextArea(); 32 | edit.setWrapStyleWord(true); 33 | edit.setLineWrap(true); 34 | edit.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); 35 | edit.addFocusListener(this); 36 | } 37 | return edit; 38 | } 39 | 40 | public void clear() { 41 | SwingUtilities.invokeLater(new Runnable() { 42 | public void run() { 43 | try { 44 | getEditor().getDocument().remove(0, getEditor().getDocument().getLength()); 45 | } catch (BadLocationException e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | }); 50 | } 51 | 52 | public String getContents() { 53 | return getEditor().getText(); 54 | } 55 | 56 | public void setContents(final String txt) { 57 | SwingUtilities.invokeLater(new Runnable() { 58 | public void run() { 59 | getEditor().setText(txt); 60 | getEditor().setCaretPosition(0); 61 | } 62 | }); 63 | } 64 | 65 | public void saveCurrentNote() { 66 | if (curDate != null) { 67 | UserManager.getCurrentUser().setNotes(getContents(), curDate); 68 | } 69 | } 70 | 71 | private String curNote = null; 72 | 73 | public void setDate(Date d) { 74 | curDate = d; 75 | clear(); 76 | curNote = UserManager.getCurrentUser().getNotes(curDate); 77 | if (curNote == null) { 78 | curNote = ""; 79 | } 80 | setContents(curNote); 81 | } 82 | 83 | public void focusGained(FocusEvent arg0) { 84 | // Do nothing 85 | } 86 | 87 | /** 88 | * Invoked when the text area loses the keyboard focus. 89 | * This will not be invoked when the user clicks on the next/previous day button. 90 | */ 91 | public void focusLost(FocusEvent e) { 92 | if (curDate != null) { 93 | saveCurrentNote(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/notes/NotesHistory.java: -------------------------------------------------------------------------------- 1 | 2 | package ca.spaz.cron.notes; 3 | 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import org.w3c.dom.Element; 8 | 9 | import ca.spaz.cron.records.History; 10 | import ca.spaz.cron.records.Record; 11 | 12 | /** 13 | * The notes (daily diary) history for a user. 14 | * 15 | * @author Aaron Davidson 16 | */ 17 | public class NotesHistory extends History { 18 | 19 | private static final String NOTES_HISTORY_FILE = "notes.xml"; 20 | 21 | 22 | public NotesHistory() { 23 | super(); 24 | } 25 | 26 | public String getBaseName() { 27 | return "notes"; 28 | } 29 | 30 | public String getEntryTagName() { 31 | return "note"; 32 | } 33 | 34 | public Record loadUserEntry(Element e) { 35 | Note note = new Note(); 36 | note.load(e); 37 | return note; 38 | } 39 | 40 | public synchronized String getNote(Date date) { 41 | String str = null; 42 | List list = getEntriesOn(date); 43 | if (list != null) { 44 | if (list.size() > 0) { 45 | Note note = (Note)list.get(0); 46 | str = note.getNote(); 47 | } 48 | } 49 | return str; 50 | } 51 | 52 | public synchronized void setNote(String note, Date d) { 53 | if (note != null && note.length() == 0) { 54 | note = null; 55 | } 56 | List list = getEntriesOn(d); 57 | if (list.size() > 0) { 58 | Note n = (Note)list.get(0); 59 | if (note == null) { 60 | deleteEntry(n); 61 | } else if (!n.getNote().equals(note)) { 62 | n.setNote(note); 63 | updateEntry(n); 64 | } 65 | } else if (note != null) { 66 | Note n = new Note(); 67 | n.setDate(d); 68 | n.setNote(note); 69 | addEntry(n); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/records/Record.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.records; 2 | 3 | import java.util.Date; 4 | 5 | import org.w3c.dom.Element; 6 | 7 | import ca.spaz.util.XMLNode; 8 | 9 | public interface Record { 10 | 11 | public Date getDate(); 12 | 13 | public XMLNode toXML(); 14 | 15 | public void load(Element e); 16 | 17 | public Record copy(); 18 | 19 | public boolean isLoaded(); 20 | } 21 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/records/RecordSelectionListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.records; 2 | 3 | 4 | public interface RecordSelectionListener { 5 | 6 | public void recordSelected(Record record); 7 | 8 | public void recordDoubleClicked(Record record); 9 | 10 | public void recordChosen(Record record); 11 | } 12 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/records/RecordTableModel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.records; 2 | 3 | import java.awt.Component; 4 | import java.text.DateFormat; 5 | import java.text.SimpleDateFormat; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import ca.spaz.gui.PrettyTable; 10 | import ca.spaz.gui.PrettyTableModel; 11 | 12 | public abstract class RecordTableModel extends PrettyTableModel { 13 | private static final DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); 14 | 15 | private List entries = new ArrayList(); 16 | 17 | public void setUserEntrys(List list) { 18 | entries = list; 19 | fireTableDataChanged(); 20 | } 21 | 22 | public List getUserEntrys() { 23 | return entries; 24 | } 25 | 26 | public Class getColumnClass(int col) { 27 | Object o = getValueAt(0, col); 28 | if (o != null) { 29 | return o.getClass(); 30 | } 31 | return String.class; 32 | } 33 | 34 | public Record getUserEntry(int i) { 35 | if (i < 0 || i >= entries.size()) { 36 | return null; 37 | } 38 | return (Record) entries.get(i); 39 | } 40 | 41 | public int getNumUserEntrys() { 42 | return entries.size(); 43 | } 44 | 45 | public int getRowCount() { 46 | return entries.size(); 47 | } 48 | 49 | public void delete(Record UserEntry) { 50 | entries.remove(UserEntry); 51 | fireTableDataChanged(); 52 | } 53 | 54 | public void addUserEntry(Record userEntry) { 55 | entries.add(userEntry); 56 | fireTableDataChanged(); 57 | } 58 | 59 | public void sort() { 60 | // no sorting in this table for now. 61 | } 62 | 63 | /** 64 | * Allows custom rendering for a row and column. Can just return c, if no 65 | * changes to default are desired. 66 | * @param c the component used for rendering the cell 67 | * @param row the row to render 68 | * @param col the column to render 69 | * @return a custom rendering component 70 | */ 71 | public Component customRender(Component c, PrettyTable table, int row, int col) { 72 | return c; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/AbstractNutrientSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | import javax.swing.*; 9 | 10 | import ca.spaz.cron.foods.NutrientInfo; 11 | import ca.spaz.cron.targets.Target; 12 | import ca.spaz.cron.user.UserManager; 13 | 14 | public abstract class AbstractNutrientSummaryPanel extends JPanel { 15 | 16 | protected NutrientTable nutrientTable; 17 | protected JScrollPane scrollPane; 18 | protected abstract List getNutrientList(); 19 | protected abstract String getCategoryName(); 20 | 21 | protected NutrientTable getNutrientTable() { 22 | if (nutrientTable == null) { 23 | nutrientTable = new NutrientTable(getNutrientList()); 24 | } 25 | return nutrientTable; 26 | } 27 | 28 | protected JScrollPane getNutrientTablePane() { 29 | if (scrollPane == null) { 30 | scrollPane = new JScrollPane(getNutrientTable()); 31 | scrollPane.setMinimumSize(new Dimension(400, 180)); 32 | scrollPane.setPreferredSize(new Dimension(500, 180)); 33 | scrollPane.getViewport().setBackground(Color.WHITE); 34 | scrollPane.setBorder(BorderFactory.createEtchedBorder()); 35 | scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 36 | } 37 | return scrollPane; 38 | } 39 | 40 | public void update(List consumed) { 41 | getNutrientTable().update(consumed); 42 | } 43 | 44 | /** 45 | * Look through all nutrients and see what overall percentage of the targets 46 | * are completed. 47 | */ 48 | public double getTargetCompletion(boolean average) { 49 | double total = 0; 50 | double value = 0; 51 | double valueFull = 0; 52 | 53 | Iterator iter = getNutrientList().iterator(); 54 | while (iter.hasNext()) { 55 | NutrientInfo ni = (NutrientInfo)iter.next(); 56 | Target target = UserManager.getCurrentUser().getTarget(ni); 57 | if (target.getMin() > 0 && UserManager.getCurrentUser().isTracking(ni)) { 58 | double amount = getNutrientTable().getAmount(ni); 59 | valueFull += amount / target.getMin(); 60 | if (amount < target.getMin()) { 61 | value += amount / target.getMin(); 62 | } else { 63 | value++; 64 | } 65 | total++; 66 | } 67 | } 68 | if (average) { 69 | return valueFull / total; 70 | } else { 71 | return value / total; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/AminoAcidSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.List; 5 | 6 | import javax.swing.BorderFactory; 7 | 8 | import ca.spaz.cron.foods.NutrientInfo; 9 | 10 | public class AminoAcidSummaryPanel extends AbstractNutrientSummaryPanel { 11 | 12 | public AminoAcidSummaryPanel() { 13 | setLayout(new BorderLayout()); 14 | setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 15 | add(getNutrientTablePane(), BorderLayout.CENTER); 16 | } 17 | 18 | protected List getNutrientList() { 19 | return NutrientInfo.getAminoAcids(); 20 | } 21 | 22 | protected String getCategoryName() { 23 | return "Amino Acids"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/LipidSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.List; 5 | 6 | import javax.swing.BorderFactory; 7 | 8 | import ca.spaz.cron.foods.NutrientInfo; 9 | 10 | public class LipidSummaryPanel extends AbstractNutrientSummaryPanel { 11 | 12 | public LipidSummaryPanel() { 13 | setLayout(new BorderLayout()); 14 | setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 15 | add(getNutrientTablePane(), BorderLayout.CENTER); 16 | } 17 | 18 | protected List getNutrientList() { 19 | return NutrientInfo.getLipids(); 20 | } 21 | 22 | protected String getCategoryName() { 23 | return "Lipids"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/MacroChart.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.JComponent; 6 | 7 | public class MacroChart extends JComponent { 8 | 9 | private double protein; 10 | private double carbs; 11 | private double fat; 12 | 13 | public double getCarbs() { 14 | return carbs; 15 | } 16 | 17 | public void setCarbs(double carbs) { 18 | this.carbs = carbs; 19 | repaint(); 20 | } 21 | 22 | public double getFat() { 23 | return fat; 24 | } 25 | 26 | public void setFat(double fat) { 27 | this.fat = fat; 28 | repaint(); 29 | } 30 | 31 | public double getProtein() { 32 | return protein; 33 | } 34 | 35 | public void setProtein(double protein) { 36 | this.protein = protein; 37 | repaint(); 38 | } 39 | 40 | public void paint(Graphics g) { 41 | Graphics2D g2d = (Graphics2D)g; 42 | g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f)); 43 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 44 | double total = protein + carbs + fat; 45 | int w = getWidth(); 46 | int h = getHeight(); 47 | int min = w < h ? w : h; 48 | g.setColor(Color.BLACK); 49 | 50 | g.setColor(Color.GREEN); 51 | int amount = 0; 52 | g.fillArc(2, 2, min - 4, min - 4, amount, (int)(360 * (protein / total))); 53 | amount += (int)(360 * (protein / total)); 54 | 55 | g.setColor(Color.BLUE); 56 | g.fillArc(2, 2, min - 4, min - 4, amount, (int)(360 * (carbs / total))); 57 | amount += (int)(360 * (carbs / total)); 58 | 59 | g.setColor(Color.RED); 60 | g.fillArc(2, 2, min - 4, min - 4, amount, (int)(360 * (fat / total))); 61 | 62 | g.setColor(Color.GREEN); 63 | g.drawString(Integer.toString((int)(100 * (protein / total))), 5, h / 2); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/MacroNutrientSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | import javax.swing.BorderFactory; 8 | 9 | import ca.spaz.cron.foods.NutrientInfo; 10 | import ca.spaz.cron.foods.Serving; 11 | 12 | public class MacroNutrientSummaryPanel extends AbstractNutrientSummaryPanel { 13 | public MacroNutrientSummaryPanel() { 14 | setLayout(new BorderLayout()); 15 | setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 16 | add(getNutrientTablePane(), BorderLayout.CENTER); 17 | } 18 | 19 | protected String getCategoryName() { 20 | return "General"; 21 | } 22 | 23 | protected List getNutrientList() { 24 | return NutrientInfo.getMacroNutrients(); 25 | } 26 | 27 | private double getAmount(List servings, NutrientInfo ni) { 28 | double total = 0; 29 | for (Iterator iter = servings.iterator(); iter.hasNext(); ) { 30 | Serving serving = (Serving) iter.next(); 31 | double weight = serving.getGrams() / 100.0; 32 | total += weight * serving.getFood().getNutrientAmount(ni); 33 | } 34 | return total; 35 | } 36 | 37 | protected NutrientTable getNutrientTable() { 38 | if (nutrientTable == null) { 39 | nutrientTable = new NutrientTable(NutrientInfo.getMacroNutrients()); 40 | } 41 | return nutrientTable; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/MineralSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.List; 5 | 6 | import javax.swing.BorderFactory; 7 | 8 | import ca.spaz.cron.foods.NutrientInfo; 9 | 10 | public class MineralSummaryPanel extends AbstractNutrientSummaryPanel { 11 | 12 | public MineralSummaryPanel() { 13 | setLayout(new BorderLayout()); 14 | setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 15 | add(getNutrientTablePane(), BorderLayout.CENTER); 16 | } 17 | 18 | protected List getNutrientList() { 19 | return NutrientInfo.getMinerals(); 20 | } 21 | 22 | protected String getCategoryName() { 23 | return "Minerals"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/SummaryFormat.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.text.DateFormat; 4 | import java.text.DecimalFormat; 5 | import java.util.*; 6 | 7 | import ca.spaz.cron.foods.NutrientInfo; 8 | import ca.spaz.cron.foods.Serving; 9 | import ca.spaz.cron.targets.Target; 10 | import ca.spaz.cron.user.UserManager; 11 | 12 | public abstract class SummaryFormat { 13 | protected DecimalFormat df = new DecimalFormat("######0.0"); 14 | protected DecimalFormat nf = new DecimalFormat("######0%"); 15 | protected DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG); 16 | 17 | public abstract String getFormatName(); 18 | 19 | public String toString() { 20 | return getFormatName(); 21 | } 22 | 23 | public abstract String export(List servings, Date start, Date end, int days, boolean targetsOnly); 24 | public abstract String exportCategory(String category, List servings, int days, boolean targetsOnly); 25 | public abstract String export(NutrientInfo ni, List servings, int days, boolean targetsOnly); 26 | 27 | public double getAmount(List servings, NutrientInfo ni) { 28 | double total = 0; 29 | for (Iterator iter = servings.iterator(); iter.hasNext(); ) { 30 | Serving serving = (Serving) iter.next(); 31 | double weight = serving.getGrams() / 100.0; 32 | total += weight * serving.getFood().getNutrientAmount(ni); 33 | } 34 | return total; 35 | } 36 | 37 | /** 38 | * Look through all nutrients and see what overall percentage of the targets 39 | * are completed. 40 | */ 41 | public double getTargetCompletion(List servings, List nutrients, int days, boolean average) { 42 | double total = 0; 43 | double value = 0; 44 | double valueFull = 0; 45 | 46 | Iterator iter = nutrients.iterator(); 47 | while (iter.hasNext()) { 48 | NutrientInfo ni = (NutrientInfo)iter.next(); 49 | Target target = UserManager.getCurrentUser().getTarget(ni); 50 | if (target.getMin() > 0 && UserManager.getCurrentUser().isTracking(ni)) { 51 | double amount = getAmount(servings, ni) / (double) days; 52 | valueFull += amount / target.getMin(); 53 | if (amount < target.getMin()) { 54 | value += amount / target.getMin(); 55 | } else { 56 | value++; 57 | } 58 | total++; 59 | } 60 | } 61 | if (average) { 62 | return valueFull / total; 63 | } else { 64 | return value / total; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/summary/VitaminSummaryPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.summary; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.List; 5 | 6 | import javax.swing.BorderFactory; 7 | 8 | import ca.spaz.cron.foods.NutrientInfo; 9 | 10 | public class VitaminSummaryPanel extends AbstractNutrientSummaryPanel { 11 | 12 | public VitaminSummaryPanel() { 13 | setLayout(new BorderLayout()); 14 | setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 15 | add(getNutrientTablePane(), BorderLayout.CENTER); 16 | } 17 | 18 | protected List getNutrientList() { 19 | return NutrientInfo.getVitamins(); 20 | } 21 | 22 | protected String getCategoryName() { 23 | return "Vitamins"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/targets/Target.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. Target and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.cron.targets; 13 | 14 | public class Target { 15 | double min, max; 16 | 17 | public Target() { 18 | } 19 | 20 | public Target(double min, double max) { 21 | this.min = min; 22 | this.max = max; 23 | } 24 | 25 | public double getMax() { 26 | return max; 27 | } 28 | 29 | public void setMax(double max) { 30 | this.max = max; 31 | } 32 | 33 | public double getMin() { 34 | return min; 35 | } 36 | 37 | public void setMin(double min) { 38 | this.min = min; 39 | } 40 | 41 | public boolean isUndefined() { 42 | return min <= 0 && max <= 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/targets/TargetEditorTable.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.targets; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.util.List; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.TableColumn; 9 | 10 | import ca.spaz.cron.user.User; 11 | import ca.spaz.gui.PrettyTable; 12 | 13 | /** 14 | * @author Aaron Davidson 15 | */ 16 | public class TargetEditorTable extends JScrollPane { 17 | 18 | TargetEditorTableModel model; 19 | PrettyTable nutrientTable; 20 | 21 | public TargetEditorTable(User user, List nutrients) { 22 | model = new TargetEditorTableModel(user, nutrients); 23 | setViewportView(getTable()); 24 | getViewport().setBackground(Color.WHITE); 25 | setPreferredSize(new Dimension(350, 200)); 26 | } 27 | 28 | private JTable getTable() { 29 | if (null == nutrientTable) { 30 | nutrientTable = new PrettyTable(model); 31 | nutrientTable.getSelectionModel().setSelectionMode( 32 | ListSelectionModel.SINGLE_SELECTION); 33 | nutrientTable.getTableHeader().setReorderingAllowed(false); 34 | for (int i = 0; i < model.getColumnCount(); i++) { 35 | TableColumn tc = nutrientTable.getTableHeader().getColumnModel().getColumn(i); 36 | tc.setPreferredWidth(model.getColumnMaxWidth(i)); 37 | } 38 | } 39 | return nutrientTable; 40 | } 41 | 42 | public void fireTargetsChanged() { 43 | model.fireTableDataChanged(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/targets/TargetModel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.targets; 2 | 3 | import ca.spaz.cron.foods.NutrientInfo; 4 | import ca.spaz.cron.user.User; 5 | 6 | /** 7 | * A Target Model can suggest targets for a User 8 | * 9 | * This lets us add different expert systems that 10 | * suggest nutrient target values for a user, 11 | * based on their parameters (age, weight, sex, etc...) 12 | * 13 | * @author Aaron Davidson 14 | */ 15 | public interface TargetModel { 16 | 17 | public double getTargetMinimum(User user, NutrientInfo ni); 18 | public double getTargetMaximum(User user, NutrientInfo ni); 19 | } 20 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/ui/ReadMe.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.ui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.net.URL; 6 | 7 | import javax.swing.ImageIcon; 8 | import javax.swing.JFrame; 9 | 10 | import ca.spaz.gui.*; 11 | 12 | public class ReadMe extends WrappedPanel { 13 | private String title; 14 | 15 | public ReadMe(JFrame parent, String title, URL url) { 16 | this.title = title; 17 | setLayout(new BorderLayout()); 18 | WebViewer wv = new WebViewer(url); 19 | wv.setPreferredSize(new Dimension(600, 400)); 20 | add(wv, BorderLayout.CENTER); 21 | WrapperDialog.showDialog(parent, this); 22 | } 23 | 24 | public String getTitle() { 25 | return title; 26 | } 27 | 28 | public String getSubtitle() { 29 | return title; 30 | } 31 | 32 | public String getInfoString() { 33 | return null; 34 | } 35 | 36 | public ImageIcon getIcon() { 37 | return null; 38 | } 39 | 40 | public boolean showSidebar() { 41 | return false; 42 | } 43 | 44 | public boolean isCancellable() { 45 | return false; 46 | } 47 | 48 | public void doCancel() { 49 | } 50 | 51 | public boolean doAccept() { 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/ui/SearchHit.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.ui; 2 | 3 | import ca.spaz.cron.datasource.Datasources; 4 | import ca.spaz.cron.datasource.FoodProxy; 5 | 6 | 7 | public class SearchHit implements Comparable { 8 | private FoodProxy fp; 9 | private int score; 10 | 11 | public SearchHit(FoodProxy fp) { 12 | this.fp = fp; 13 | } 14 | 15 | /** 16 | * A heuristic scoring function to give a smart sort of the results 17 | * @param query the user's search terms, as entered 18 | */ 19 | public void computeScore(String[] query) { 20 | score = 100; // base score, so we don't go negative 21 | 22 | // start off with big bonus for having a history of being used 23 | score += fp.getReferences() * 100; 24 | 25 | // I hate how USDA is full of babyfoods. Come on, really. 26 | if (fp.getDescription().startsWith("Babyfood")) { 27 | score -= 100; 28 | } 29 | 30 | if (fp.getSource() == Datasources.getUserFoods()) { 31 | score += 100; 32 | } else if (fp.getSource() == Datasources.getCRDBFoods()) { 33 | score += 50; 34 | } 35 | 36 | // penalize longer strings. We're usually searching for short, simple foods. 37 | score -= 3 * getFoodProxy().getDescription().length(); 38 | 39 | // add bonus for search terms being early in the description 40 | for (int i = 0; i < query.length; i++) { 41 | score += 50 * (1.0 - (fp.getDescription().indexOf(query[i]) / (double)fp.getDescription().length())); 42 | } 43 | 44 | if (fp.isDeprecated()) { 45 | score -= 200; 46 | } 47 | 48 | } 49 | 50 | public int getScore() { 51 | return score; 52 | } 53 | public FoodProxy getFoodProxy() { 54 | return fp; 55 | } 56 | 57 | public int compareTo(Object obj) { 58 | SearchHit hit = (SearchHit) obj; 59 | 60 | if (getScore() > hit.getScore()) { 61 | return -1; 62 | } 63 | if (getScore() < hit.getScore()) { 64 | return 1; 65 | } 66 | 67 | // break tie with alphabetical order 68 | return getFoodProxy().getDescription().compareToIgnoreCase(hit.getFoodProxy().getDescription()); 69 | } 70 | 71 | public int compareByName(Object obj) { 72 | SearchHit hit = (SearchHit) obj; 73 | return getFoodProxy().getDescription().compareToIgnoreCase(hit.getFoodProxy().getDescription()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/user/Favorites.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.cron.user; 2 | 3 | 4 | public class Favorites { 5 | private static final int MAX_SLOTS = 1000; 6 | 7 | public class Favorite implements Comparable { 8 | Object val; 9 | int rank; 10 | 11 | public int compareTo(Object arg0) { 12 | return 0; 13 | } 14 | } 15 | 16 | private Favorite[] favorites = new Favorite[MAX_SLOTS]; 17 | } 18 | -------------------------------------------------------------------------------- /src/ca/spaz/cron/user/UserChangeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. UserChangeListener and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.cron.user; 13 | 14 | /** 15 | * An interface to be implemented by objects interested in user model changes. 16 | * @author Chris Rose 17 | */ 18 | public interface UserChangeListener { 19 | 20 | /** 21 | * This event is fired whenever the user model changes in some way. 22 | * @param userMan the User that has changed. 23 | */ 24 | void userChanged(UserManager userMan); 25 | } 26 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/DateChooser.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.util.Date; 4 | 5 | import javax.swing.ImageIcon; 6 | import javax.swing.JComponent; 7 | 8 | import org.jdesktop.swingx.JXMonthView; 9 | 10 | 11 | public class DateChooser extends WrappedPanel { 12 | private String title = "Choose Date"; 13 | 14 | private JXMonthView cal; 15 | 16 | public DateChooser(Date curDate) { 17 | cal = new JXMonthView(); 18 | cal.setTraversable(true); 19 | cal.setSelectionInterval(curDate, curDate); 20 | 21 | add(cal); 22 | } 23 | 24 | public static Date pickDate(JComponent parent, Date d) { 25 | DateChooser dc = new DateChooser(d); 26 | WrapperDialog.showDialog(parent, dc); 27 | return dc.cal.getSelection().first(); 28 | } 29 | 30 | public static Date pickDate(JComponent parent, Date d, String title) { 31 | DateChooser dc = new DateChooser(d); 32 | dc.setTitle(title); 33 | WrapperDialog.showDialog(parent, dc); 34 | return dc.cal.getSelection().first(); 35 | } 36 | 37 | private void setTitle(String title) { 38 | this.title = title; 39 | } 40 | 41 | public String getTitle() { 42 | return title; 43 | } 44 | 45 | public String getSubtitle() { 46 | return null; 47 | } 48 | 49 | public String getInfoString() { 50 | return title; 51 | } 52 | 53 | public ImageIcon getIcon() { 54 | return null; 55 | } 56 | 57 | public boolean showSidebar() { 58 | return false; 59 | } 60 | 61 | public boolean isCancellable() { 62 | return false; 63 | } 64 | 65 | public void doCancel() { 66 | } 67 | 68 | public boolean doAccept() { 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/DoubleField.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.Toolkit; 4 | import java.text.DecimalFormat; 5 | 6 | import javax.swing.JTextField; 7 | import javax.swing.text.*; 8 | 9 | import ca.spaz.util.ToolBox; 10 | 11 | /** 12 | * A text field for entering floating point data. The only characters it accepts 13 | * are the digits 0-9 and one (optional) decimal point. 14 | * 15 | * @author Aaron 16 | * 17 | */ 18 | public class DoubleField extends JTextField { 19 | private Toolkit toolkit; 20 | 21 | private double min = Integer.MIN_VALUE, max = Integer.MAX_VALUE; 22 | 23 | private DecimalFormat df = new DecimalFormat("#########0.0###"); 24 | 25 | public DoubleField(double value, int columns) { 26 | super(columns); 27 | setHorizontalAlignment(JTextField.RIGHT); 28 | toolkit = Toolkit.getDefaultToolkit(); 29 | setValue(value); 30 | } 31 | 32 | public void setRange(double min, double max) { 33 | this.min = min; 34 | this.max = max; 35 | } 36 | 37 | public double getValue() { 38 | double retVal = 0.0; 39 | String[] q; 40 | double[] r = {0.0, 0.0}; 41 | q = getText().split("/", 2); 42 | try { 43 | for (int i = 0; i < q.length; i++) { 44 | r[i] = Double.parseDouble(q[i]); 45 | } 46 | } catch (NumberFormatException e) { 47 | } 48 | retVal = ToolBox.safeDivide(r[0], r[1], r[0]); 49 | if (retVal < min) { 50 | retVal = min; 51 | } 52 | if (retVal > max) { 53 | retVal = max; 54 | } 55 | return retVal; 56 | } 57 | 58 | public void setValue(double value) { 59 | if (value < min) { 60 | value = min; 61 | } 62 | if (value > max) { 63 | value = max; 64 | } 65 | if (value == 0.0) { 66 | setText(""); 67 | } else { 68 | setText(df.format(value)); 69 | } 70 | selectAll(); 71 | } 72 | 73 | public void setValue(String value) { 74 | setText(value); 75 | } 76 | 77 | protected Document createDefaultModel() { 78 | return new DoubleDocument(); 79 | } 80 | 81 | protected String getCurrentText() { 82 | return getText(); 83 | } 84 | 85 | protected class DoubleDocument extends PlainDocument { 86 | // Overridden to allow only digits and one decimal point e.g. 1.00 87 | // Note that not all data that would allowed by Double.parseDouble() is 88 | // supported, e.g. 89 | // exponential notation, negative signs, hex notation etc. 90 | public void insertString(int offs, String str, AttributeSet a) 91 | throws BadLocationException { 92 | char[] source = str.toCharArray(); 93 | char[] result = new char[source.length]; 94 | int j = 0; 95 | for (int i = 0; i < result.length; i++) { 96 | char c = source[i]; 97 | if (Character.isDigit(c) 98 | || (c == '.' && getCurrentText().indexOf('.') == -1) 99 | || (c == '/' && getCurrentText().indexOf('/') == -1)) { 100 | result[j++] = c; 101 | } else { 102 | toolkit.beep(); 103 | } 104 | } 105 | super.insertString(offs, new String(result, 0, j), a); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/ErrorReporter.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Component; 5 | 6 | import javax.swing.*; 7 | 8 | import ca.spaz.util.ImageFactory; 9 | 10 | public class ErrorReporter extends WrappedPanel { 11 | private String message; 12 | 13 | public ErrorReporter(Exception e) { 14 | StringBuffer sb = new StringBuffer(); 15 | if (e != null) { 16 | sb.append("
"); 17 | sb.append("" + e.toString() + "

"); 18 | sb.append("
"); 19 | for (int i = 0; i < e.getStackTrace().length && i < 8; i++) { 20 | sb.append(e.getStackTrace()[i].toString() + "
"); 21 | } 22 | sb.append("
"); 23 | sb.append("
"); 24 | } 25 | 26 | JLabel lbl = new JLabel(sb.toString(), JLabel.CENTER); 27 | setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); 28 | setLayout(new BorderLayout(4, 4)); 29 | add(lbl, BorderLayout.CENTER); 30 | } 31 | 32 | public static void showError(Exception e, Component parent) { 33 | e.printStackTrace(); 34 | ErrorReporter er = new ErrorReporter(e); 35 | WrapperDialog.showDialog(parent, er); 36 | } 37 | 38 | public static void showError(String message, Exception e, Component parent) { 39 | e.printStackTrace(); 40 | ErrorReporter er = new ErrorReporter(e); 41 | er.message = message; 42 | WrapperDialog.showDialog(parent, er); 43 | } 44 | 45 | public static void showError(String message, Component parent) { 46 | ErrorReporter er = new ErrorReporter(null); 47 | er.message = message; 48 | WrapperDialog.showDialog(parent, er); 49 | } 50 | 51 | public static void showNotYetImplemented(Component parent) { 52 | ErrorReporter er = new ErrorReporter(null); 53 | er.message = "Not Yet Implemented"; 54 | WrapperDialog.showDialog(parent, er); 55 | } 56 | 57 | public String getTitle() { 58 | return "Error"; 59 | } 60 | 61 | public String getSubtitle() { 62 | if (message != null) { 63 | return message; 64 | } 65 | return "An Unexpected Error Occurred"; 66 | } 67 | 68 | public String getInfoString() { 69 | return "Unexpected Error"; 70 | } 71 | 72 | public ImageIcon getIcon() { 73 | return new ImageIcon(ImageFactory.getInstance().loadImage("/img/apple-100x100.png")); 74 | } 75 | 76 | public boolean isCancellable() { 77 | return false; 78 | } 79 | 80 | public void doCancel() { 81 | } 82 | 83 | public boolean doAccept() { 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/HyperLabel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.Cursor; 4 | import java.awt.event.*; 5 | import java.util.Vector; 6 | 7 | import javax.swing.JLabel; 8 | 9 | public class HyperLabel extends JLabel implements MouseListener { 10 | private static final String BLUE = "#0000FF"; 11 | private static final String RED = "#FF0000"; 12 | private static final String GRAY = "#AAAAAA"; 13 | 14 | private String color = BLUE; 15 | private String text = ""; 16 | 17 | private Vector listeners = new Vector(); 18 | 19 | public HyperLabel() { 20 | init(); 21 | } 22 | 23 | public HyperLabel(String text) { 24 | super(); 25 | setText(text, true); 26 | init(); 27 | } 28 | 29 | private void init() { 30 | addMouseListener(this); 31 | setEnabled(true); 32 | setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 33 | } 34 | 35 | public void setEnabled(boolean val) { 36 | super.setEnabled(val); 37 | color = val ? BLUE : GRAY; 38 | setText(text); 39 | } 40 | 41 | public void setText(String text) { 42 | setText(text, true); 43 | } 44 | 45 | public void setText(String text, boolean doBold) { 46 | this.text = text; 47 | super.setText("" + (doBold ? "" : "") + "" + text + "" + (doBold ? "" : "") + ""); 48 | } 49 | 50 | public void mouseClicked(MouseEvent e) { 51 | if (isEnabled()) { 52 | ActionEvent ae = new ActionEvent(this, 0, null); 53 | for (int i = 0; i < listeners.size(); i++) { 54 | ActionListener al = (ActionListener)listeners.get(i); 55 | al.actionPerformed(ae); 56 | } 57 | } 58 | } 59 | 60 | public String getPlainText() { 61 | return text; 62 | } 63 | 64 | public void mouseEntered(MouseEvent e) { 65 | } 66 | 67 | public void mouseExited(MouseEvent e) { 68 | } 69 | 70 | public void mousePressed(MouseEvent e) { 71 | } 72 | 73 | public void mouseReleased(MouseEvent e) { 74 | } 75 | 76 | public void addActionListener(ActionListener al) { 77 | listeners.add(al); 78 | } 79 | 80 | public void removeActionListener(ActionListener al) { 81 | listeners.remove(al); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/IconFont.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.Component; 4 | import java.awt.Font; 5 | import java.io.InputStream; 6 | 7 | import javax.swing.JButton; 8 | 9 | 10 | public class IconFont { 11 | 12 | private static Font iconFont; 13 | 14 | public static JButton createIconFontButton(String code, 15 | Component component) { 16 | JButton button = new JButton(code); 17 | button.setFont(getIconFont(component)); 18 | return button; 19 | } 20 | 21 | private static Font getIconFont(Component component) { 22 | if (iconFont == null) 23 | { 24 | try { 25 | InputStream in = component.getClass().getResourceAsStream( 26 | "/fontawesome.tff"); 27 | 28 | Font base = Font.createFont(Font.TRUETYPE_FONT, in); 29 | iconFont = base.deriveFont(Font.PLAIN, 16); 30 | } catch (java.lang.Exception exception) { 31 | ErrorReporter.showError(exception, component); 32 | iconFont = new Font("Application", Font.PLAIN, 16); 33 | } 34 | } 35 | 36 | return iconFont; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/IntegerField.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.Toolkit; 4 | 5 | import javax.swing.JTextField; 6 | import javax.swing.text.*; 7 | 8 | /** 9 | * A text field for entering integer numeric data. The only characters it accepts are the 10 | * digits 0-9. 11 | * @author Gerald 12 | * 13 | */ 14 | public class IntegerField extends JTextField { 15 | private Toolkit toolkit; 16 | private int min = Integer.MIN_VALUE, max = Integer.MAX_VALUE; 17 | 18 | public IntegerField(int value, int columns) { 19 | super(columns); 20 | setHorizontalAlignment(JTextField.RIGHT); 21 | toolkit = Toolkit.getDefaultToolkit(); 22 | setValue(value); 23 | } 24 | 25 | public void setRange(int min, int max) { 26 | this.min = min; 27 | this.max = max; 28 | } 29 | 30 | public int getValue() { 31 | int retVal = 0; 32 | try { 33 | retVal = Integer.parseInt(getText()); 34 | } catch (NumberFormatException e) { 35 | } 36 | if (retVal < min) { 37 | retVal = min; 38 | } 39 | if (retVal > max) { 40 | retVal = max; 41 | } 42 | return retVal; 43 | } 44 | 45 | public void setValue(int value) { 46 | if (value < min) { 47 | value = min; 48 | } 49 | if (value > max) { 50 | value = max; 51 | } 52 | if (value == 0) { 53 | setText(""); 54 | } else { 55 | setText(Integer.toString(value)); 56 | } 57 | selectAll(); 58 | } 59 | 60 | public void setValue(String value) { 61 | setText(value); 62 | } 63 | 64 | protected Document createDefaultModel() { 65 | return new IntegerDocument(); 66 | } 67 | 68 | protected String getCurrentText() { 69 | return getText(); 70 | } 71 | 72 | protected class IntegerDocument extends PlainDocument { 73 | // Overidden to allow only digits and a hyphen (minus sign, only if min < 0) 74 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { 75 | char[] source = str.toCharArray(); 76 | char[] result = new char[source.length]; 77 | int j = 0; 78 | for (int i = 0; i < result.length; i++) { 79 | char c = source[i]; 80 | if (Character.isDigit(c) || (c == '-' && min < 0 && getCurrentText().indexOf('-') == -1)) { 81 | result[j++] = c; 82 | } else { 83 | toolkit.beep(); 84 | } 85 | } 86 | super.insertString(offs, new String(result, 0, j), a); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/JMonthChooser.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import javax.swing.DefaultComboBoxModel; 4 | import javax.swing.JComboBox; 5 | 6 | public class JMonthChooser extends JComboBox { 7 | 8 | public static final String[] MONTHS = { 9 | "January", 10 | "February", 11 | "March", 12 | "April", 13 | "May", 14 | "June", 15 | "July", 16 | "August", 17 | "September", 18 | "October", 19 | "November", 20 | "December" 21 | }; 22 | 23 | public JMonthChooser() { 24 | setModel(new DefaultComboBoxModel(MONTHS)); 25 | } 26 | 27 | public void setMonth(int i) { 28 | setSelectedIndex(i); 29 | } 30 | 31 | public int getMonth() { 32 | return getSelectedIndex(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/JYearChooser.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import javax.swing.JSpinner; 4 | import javax.swing.SpinnerNumberModel; 5 | 6 | public class JYearChooser extends JSpinner { 7 | 8 | private SpinnerNumberModel numberModel; 9 | 10 | public JYearChooser() { 11 | numberModel = new SpinnerNumberModel(1950, 1800, 2100, 1); 12 | setEditor(new JSpinner.NumberEditor(this, "#")); 13 | setModel(numberModel); 14 | } 15 | 16 | public int getYear() { 17 | return ((Number)getValue()).intValue(); 18 | } 19 | 20 | public void setYear(int i) { 21 | setValue(new Integer(i)); 22 | } 23 | 24 | public void setStartYear(int i) { 25 | numberModel.setMinimum(new Integer(i)); 26 | } 27 | 28 | public void setEndYear(int i) { 29 | numberModel.setMaximum(new Integer(i)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/PercentageBar.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.JComponent; 6 | import javax.swing.border.Border; 7 | 8 | public class PercentageBar extends JComponent { 9 | private double value; 10 | private float alpha = 0.65f; 11 | 12 | public PercentageBar() { 13 | setForeground(new Color(50, 120, 160)); 14 | setPreferredSize(new Dimension(50, 8)); 15 | } 16 | 17 | public double getValue() { 18 | return value; 19 | } 20 | 21 | public void setValue(double value) { 22 | this.value = value; 23 | } 24 | 25 | public void paint(Graphics g) { 26 | int w = getWidth(); 27 | int h = getHeight(); 28 | int xo = 0; 29 | int yo = 0; 30 | Border border = getBorder(); 31 | if (border != null) { 32 | Insets insets = border.getBorderInsets(this); 33 | w -= insets.left + insets.right; 34 | h -= insets.top + insets.bottom; 35 | xo = insets.left; 36 | yo = insets.top; 37 | } 38 | 39 | Graphics2D g2d = (Graphics2D)g; 40 | g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 41 | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 42 | 43 | g.setColor(getBackground()); 44 | g.fillRect(xo, yo, w, h); 45 | 46 | g.setColor(getForeground()); 47 | int bar = (int)Math.round(w * value); 48 | if (bar > 1) { 49 | if (bar > w) { 50 | bar = w; 51 | g.setColor(getForeground().brighter()); 52 | } 53 | g.fill3DRect(xo, yo, bar, h - 1, true); 54 | } 55 | 56 | g.setColor(Color.BLACK); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/PrettyTableModel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.Component; 4 | 5 | import javax.swing.table.AbstractTableModel; 6 | 7 | public abstract class PrettyTableModel extends AbstractTableModel { 8 | private int sortOnColumn = -1; 9 | private boolean ascending = false; 10 | private boolean allowSorting = false; 11 | 12 | public abstract void sort(); 13 | 14 | public abstract String getToolTipText(int r, int c); 15 | 16 | /** 17 | * Allows custom rendering for a row and column. Can just return c, if no 18 | * changes to default are desired. 19 | * @param c the component used for rendering the cell 20 | * @param row the row to render 21 | * @param col the column to render 22 | * @return a custom rendering component 23 | */ 24 | public Component customRender(Component c, PrettyTable table, int row, int col) { 25 | return c; 26 | } 27 | 28 | public void setAllowSorting(boolean val) { 29 | allowSorting = val; 30 | } 31 | 32 | public boolean getAllowSorting() { 33 | return allowSorting; 34 | } 35 | 36 | public void sort(int col) { 37 | if (getSortOnColumn() == col) { 38 | setAscending(!isAscending()); 39 | } else { 40 | setAscending(isAscending()); 41 | } 42 | setSortOnColumn(col); 43 | sort(); 44 | } 45 | 46 | public boolean isAscending() { 47 | return ascending; 48 | } 49 | 50 | public void setAscending(boolean ascending) { 51 | this.ascending = ascending; 52 | } 53 | 54 | public int getSortOnColumn() { 55 | return sortOnColumn; 56 | } 57 | 58 | public void setSortOnColumn(int sortOnColumn) { 59 | this.sortOnColumn = sortOnColumn; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/SpazCanvasLoader.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import javax.swing.JComponent; 4 | import javax.swing.JPanel; 5 | 6 | 7 | /** 8 | * An XML definition of a GUI layout. 9 | * 10 | * @author Aaron Davidson 11 | * @date September 2002 12 | */ 13 | 14 | public class SpazCanvasLoader extends SpazCanvas { 15 | public JPanel proxy; 16 | 17 | public SpazCanvasLoader(String file, JPanel proxy) { 18 | super(file, null); 19 | this.proxy = proxy; 20 | this.actions = null; 21 | } 22 | 23 | public void add(JComponent c, SpazPosition lp) { 24 | proxy.add(c, lp); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/StatusBar.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | 7 | import javax.swing.*; 8 | 9 | public class StatusBar extends JPanel { 10 | private JLabel statusLabel, hoverLabel; 11 | 12 | public StatusBar() { 13 | setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 14 | setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); 15 | setForeground(Color.LIGHT_GRAY); 16 | add(Box.createHorizontalStrut(10)); 17 | add(getStatusLabel()); 18 | add(Box.createHorizontalStrut(10)); 19 | add(makeSeparator()); 20 | add(Box.createHorizontalStrut(10)); 21 | add(getHoverLabel()); 22 | add(Box.createHorizontalStrut(10)); 23 | add(makeSeparator()); 24 | add(Box.createHorizontalStrut(10)); 25 | add(Box.createHorizontalGlue()); 26 | } 27 | 28 | private JSeparator makeSeparator() { 29 | JSeparator sep = new JSeparator(JSeparator.VERTICAL); 30 | sep.setMaximumSize(new Dimension(3, 12)); 31 | return sep; 32 | } 33 | 34 | private JLabel getStatusLabel() { 35 | if (statusLabel == null) { 36 | statusLabel = new JLabel("Status"); 37 | statusLabel.setFont(statusLabel.getFont().deriveFont(10.0f)); 38 | statusLabel.setForeground(Color.DARK_GRAY); 39 | } 40 | return statusLabel; 41 | } 42 | 43 | private JLabel getHoverLabel() { 44 | if (hoverLabel == null) { 45 | hoverLabel = new JLabel(""); 46 | hoverLabel.setFont(hoverLabel.getFont().deriveFont(10.0f)); 47 | hoverLabel.setForeground(Color.DARK_GRAY); 48 | } 49 | return hoverLabel; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/TransferActionListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.KeyboardFocusManager; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | import java.beans.PropertyChangeEvent; 7 | import java.beans.PropertyChangeListener; 8 | 9 | import javax.swing.Action; 10 | import javax.swing.JComponent; 11 | 12 | public class TransferActionListener implements ActionListener, 13 | PropertyChangeListener { 14 | private JComponent focusOwner = null; 15 | 16 | public TransferActionListener() { 17 | KeyboardFocusManager manager = KeyboardFocusManager 18 | .getCurrentKeyboardFocusManager(); 19 | manager.addPropertyChangeListener("permanentFocusOwner", this); 20 | } 21 | 22 | public void propertyChange(PropertyChangeEvent e) { 23 | Object o = e.getNewValue(); 24 | if (o instanceof JComponent) { 25 | focusOwner = (JComponent)o; 26 | } else { 27 | focusOwner = null; 28 | } 29 | } 30 | 31 | public void actionPerformed(ActionEvent e) { 32 | if (focusOwner == null) { 33 | return; 34 | } 35 | String action = e.getActionCommand(); 36 | Action a = focusOwner.getActionMap().get(action); 37 | if (a != null) { 38 | a.actionPerformed(new ActionEvent(focusOwner, 39 | ActionEvent.ACTION_PERFORMED, null)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/TranslucentLabel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.Icon; 6 | import javax.swing.JLabel; 7 | 8 | public class TranslucentLabel extends JLabel { 9 | double transparency = 1; 10 | 11 | public TranslucentLabel(double transparency, String title) { 12 | super(title); 13 | this.transparency = transparency; 14 | setOpaque(false); 15 | } 16 | 17 | public TranslucentLabel(double transparency, String title, int alignment) { 18 | super(title, alignment); 19 | this.transparency = transparency; 20 | setOpaque(false); 21 | } 22 | 23 | public TranslucentLabel(double transparency, Icon icon) { 24 | super(icon); 25 | this.transparency = transparency; 26 | setOpaque(false); 27 | } 28 | 29 | public void setTransparency(double val) { 30 | this.transparency = val; 31 | } 32 | 33 | public void paint(Graphics g) { 34 | Graphics2D g2d = (Graphics2D)g; 35 | g2d.setRenderingHint( 36 | RenderingHints.KEY_ANTIALIASING, 37 | RenderingHints.VALUE_ANTIALIAS_ON); 38 | 39 | Composite c = g2d.getComposite(); 40 | g2d.setComposite(AlphaComposite.getInstance( 41 | AlphaComposite.SRC_OVER, (float)transparency)); 42 | g2d.setColor(getBackground()); 43 | g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 15, 15); 44 | g2d.setComposite(c); 45 | super.paint(g); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/TranslucentPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.JPanel; 6 | 7 | public class TranslucentPanel extends JPanel { 8 | double transparency = 1; 9 | 10 | public TranslucentPanel(double transparency) { 11 | super(); 12 | this.transparency = transparency; 13 | setOpaque(false); 14 | } 15 | 16 | public void paint(Graphics g) { 17 | Graphics2D g2d = (Graphics2D)g; 18 | g2d.setRenderingHint( 19 | RenderingHints.KEY_ANTIALIASING, 20 | RenderingHints.VALUE_ANTIALIAS_ON); 21 | Composite c = g2d.getComposite(); 22 | g2d.setComposite(AlphaComposite.getInstance( 23 | AlphaComposite.SRC_OVER, (float)transparency)); 24 | g2d.setColor(getBackground()); 25 | g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 15, 15); 26 | g2d.setComposite(c); 27 | super.paint(g); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/TranslucentToolBar.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.JToolBar; 6 | 7 | public class TranslucentToolBar extends JToolBar { 8 | double transparency = 1; 9 | 10 | public TranslucentToolBar(double transparency) { 11 | super(); 12 | this.transparency = transparency; 13 | setFloatable(false); 14 | setRollover(true); 15 | setOpaque(false); 16 | } 17 | 18 | public void setTransparency(double value) { 19 | transparency = value; 20 | 21 | // FIXME: Not sure which one to call. 22 | invalidate(); 23 | updateUI(); 24 | } 25 | 26 | public void paint(Graphics g) { 27 | Graphics2D g2d = (Graphics2D)g; 28 | g2d.setRenderingHint( 29 | RenderingHints.KEY_ANTIALIASING, 30 | RenderingHints.VALUE_ANTIALIAS_ON); 31 | Composite c = g2d.getComposite(); 32 | g2d.setComposite(AlphaComposite.getInstance( 33 | AlphaComposite.SRC_OVER, (float)transparency)); 34 | g2d.setColor(getBackground()); 35 | g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 15, 15); 36 | g2d.setComposite(c); 37 | super.paint(g); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ca/spaz/gui/WrappedPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.gui; 2 | 3 | import javax.swing.ImageIcon; 4 | import javax.swing.JPanel; 5 | 6 | public abstract class WrappedPanel extends JPanel { 7 | public abstract String getTitle(); 8 | public abstract String getSubtitle(); 9 | public abstract String getInfoString(); 10 | public abstract ImageIcon getIcon(); 11 | 12 | public abstract boolean isCancellable(); 13 | public abstract void doCancel(); 14 | public abstract boolean doAccept(); 15 | 16 | public boolean showSidebar() { 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ca/spaz/lists/ListListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.lists; 2 | 3 | public interface ListListener { 4 | 5 | /** 6 | * An unspecified change has occurred to the list. 7 | * Listeners should recompute from scratch 8 | * Only sent if a more specific event is not available. 9 | */ 10 | public void listChanged(); 11 | 12 | /** 13 | * A new item has been added to the list. 14 | * @param o the added items 15 | */ 16 | public void itemAdded(Object o); 17 | 18 | 19 | /** 20 | * A item has been removed from the list. 21 | * @param o the removed item 22 | */ 23 | public void itemRemoved(Object o); 24 | 25 | /** 26 | * The item in the list has been modified in some way 27 | * @param o the modified item 28 | */ 29 | public void itemChanged(Object o); 30 | } 31 | -------------------------------------------------------------------------------- /src/ca/spaz/lists/SmartList.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.lists; 2 | 3 | import java.util.*; 4 | 5 | 6 | public class SmartList extends ArrayList { 7 | 8 | private List listeners; // lazy initilized 9 | private boolean suppressEvents; 10 | 11 | public SmartList() { 12 | } 13 | 14 | public SmartList(List copy) { 15 | addAll(copy); 16 | } 17 | 18 | public synchronized void addAll(List copy) { 19 | super.addAll(copy); 20 | fireListChangedEvent(); 21 | } 22 | 23 | public synchronized boolean add(Object o) { 24 | boolean r = super.add(o); 25 | fireItemAddedEvent(o); 26 | return r; 27 | } 28 | 29 | public synchronized boolean remove(Object o) { 30 | boolean r = super.remove(o); 31 | fireItemRemovedEvent(o); 32 | return r; 33 | } 34 | 35 | public synchronized Object remove(int i) { 36 | Object o = super.remove(i); 37 | fireItemRemovedEvent(o); 38 | return o; 39 | } 40 | 41 | public synchronized void clear() { 42 | super.clear(); 43 | fireListChangedEvent(); 44 | } 45 | 46 | private synchronized List getListeners() { 47 | if (listeners == null) { 48 | listeners = new Vector(); 49 | } 50 | return listeners; 51 | } 52 | 53 | public synchronized void setSuppressEvents(boolean val) { 54 | suppressEvents = val; 55 | } 56 | 57 | public synchronized void addListListener(ListListener sll) { 58 | getListeners().add(sll); 59 | } 60 | 61 | public synchronized void removeListListener(ListListener sll) { 62 | getListeners().remove(sll); 63 | } 64 | 65 | public synchronized void fireListChangedEvent() { 66 | if (suppressEvents || listeners == null) { 67 | return; 68 | } 69 | Iterator iter = getListeners().iterator(); 70 | while (iter.hasNext()) { 71 | ((ListListener)iter.next()).listChanged(); 72 | } 73 | } 74 | 75 | public synchronized void fireItemRemovedEvent(Object o) { 76 | if (suppressEvents || listeners == null) { 77 | return; 78 | } 79 | Iterator iter = getListeners().iterator(); 80 | while (iter.hasNext()) { 81 | ((ListListener)iter.next()).itemRemoved(o); 82 | } 83 | } 84 | 85 | public synchronized void fireItemAddedEvent(Object o) { 86 | if (suppressEvents || listeners == null) { 87 | return; 88 | } 89 | Iterator iter = getListeners().iterator(); 90 | while (iter.hasNext()) { 91 | ((ListListener)iter.next()).itemAdded(o); 92 | } 93 | } 94 | 95 | public synchronized void fireItemChangedEvent(Object o) { 96 | if (suppressEvents || listeners == null) { 97 | return; 98 | } 99 | Iterator iter = getListeners().iterator(); 100 | while (iter.hasNext()) { 101 | ((ListListener)iter.next()).itemChanged(o); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/Columns.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. Columns and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.sql; 13 | 14 | /** 15 | * TODO describe 16 | * @author Chris Rose 17 | */ 18 | public interface Columns { 19 | 20 | SQLColumnSet getColumns(); 21 | } 22 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/DBRow.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.lang.reflect.Field; 4 | import java.sql.ResultSet; 5 | import java.sql.SQLException; 6 | 7 | /** 8 | * Simple Java Object / Relational Database linker 9 | * Uses reflection to map between simple objects and corresponding table rows. 10 | * 11 | * @author davidson 12 | */ 13 | public abstract class DBRow { 14 | 15 | /** 16 | * Attempts to load all fields in the object from a ResultSet row. 17 | * @param row a row in a database that corresponds directly to the object 18 | * @throws SQLException 19 | * @throws IllegalArgumentException 20 | * @throws IllegalAccessException 21 | */ 22 | public static void load(ResultSet row, Object obj) throws SQLException, IllegalArgumentException, IllegalAccessException { 23 | Field[] fields = obj.getClass().getFields(); 24 | for (int i = 0; i < fields.length; i++) { 25 | String name = fields[i].getName(); 26 | Class type = fields[i].getType(); 27 | 28 | if (type == String.class) { 29 | fields[i].set(obj, row.getObject(name)); 30 | } else if (type == int.class) { 31 | fields[i].setInt(obj, row.getInt(name)); 32 | } else if (type == double.class) { 33 | fields[i].setDouble(obj, row.getDouble(name)); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLColumnSet.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * ****************************************************************************** 3 | * Copyright (c) 2005 Chris Rose and AIMedia All rights reserved. SQLColumnSet 4 | * and the accompanying materials are made available under the terms of the 5 | * Common Public License v1.0 which accompanies this distribution, and is 6 | * available at http://www.eclipse.org/legal/cpl-v10.html Contributors: Chris 7 | * Rose 8 | ******************************************************************************/ 9 | package ca.spaz.sql; 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * TODO describe 15 | * 16 | * @author Chris Rose 17 | */ 18 | public class SQLColumnSet { 19 | 20 | private List terms; 21 | 22 | private List names; 23 | 24 | /** 25 | * 26 | */ 27 | public SQLColumnSet() { 28 | super(); 29 | names = new ArrayList(); 30 | terms = new ArrayList(); 31 | } 32 | 33 | public void add(String name, String str) { 34 | names.add(name); 35 | terms.add(str); 36 | } 37 | 38 | public void add(String name, Object o) { 39 | names.add(name); 40 | terms.add(SQLStatement.fixClass(o)); 41 | } 42 | 43 | public void add(String name, int i) { 44 | names.add(name); 45 | terms.add(new Integer(i)); 46 | } 47 | 48 | public void add(String name, boolean b) { 49 | names.add(name); 50 | terms.add(new Boolean(b)); 51 | } 52 | 53 | public void add(String name, double d) { 54 | names.add(name); 55 | terms.add(new Double(d)); 56 | } 57 | 58 | public void add(String name, long d) { 59 | names.add(name); 60 | terms.add(new Long(d)); 61 | } 62 | 63 | public void add(String name, float f) { 64 | names.add(name); 65 | terms.add(new Double(f)); 66 | } 67 | 68 | public void add(String name, char c) { 69 | names.add(name); 70 | terms.add(new Character(c)); 71 | } 72 | 73 | public String getNameString() { 74 | StringBuffer sb = new StringBuffer(); 75 | sb.append(" ( "); 76 | for (int i = 0; i < names.size(); i++) { 77 | Object name = names.get(i); 78 | sb.append(name.toString()); 79 | if (i < names.size() - 1) { 80 | sb.append(", "); 81 | } 82 | } 83 | sb.append(" ) "); 84 | return sb.toString(); 85 | } 86 | 87 | public String getValueString() { 88 | StringBuffer sb = new StringBuffer(); 89 | sb.append(" ("); 90 | for (int i = 0; i < terms.size(); i++) { 91 | Object term = terms.get(i); 92 | if (term != null) { 93 | sb.append(" '"); 94 | sb.append(SQLStatement.escape(term.toString())); 95 | sb.append("' "); 96 | } else { 97 | sb.append(" NULL"); 98 | } 99 | if (i < terms.size() - 1) { 100 | sb.append(", "); 101 | } 102 | } 103 | sb.append(") "); 104 | return sb.toString(); 105 | } 106 | 107 | public List getNames() { 108 | return Collections.unmodifiableList(names); 109 | } 110 | 111 | public List getValues() { 112 | return Collections.unmodifiableList(terms); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLDelete.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.sql.*; 4 | 5 | import ca.spaz.util.Logger; 6 | 7 | /** 8 | * Conveniently produces simple SQL DELETE statements 9 | * 10 | * @author Aaron Davidson 11 | */ 12 | public class SQLDelete extends SQLSelectableStatement { 13 | 14 | /** 15 | * Create a new SQLUpdate command for the given table 16 | * @param tableName the name of the table to update on 17 | */ 18 | public SQLDelete(String tableName) { 19 | super(tableName, true, false, true); 20 | } 21 | 22 | /** 23 | * Overrides execute() and calls executeUpdate() 24 | */ 25 | protected void doExecute(Connection con) throws SQLException { 26 | Statement stmt = con.createStatement(); 27 | String query = this.getQueryString(); 28 | if (Logger.isDebugEnabled()) { 29 | Logger.debug("executeQuery() - Statement to be executed: " + query); 30 | } 31 | 32 | stmt.execute(query); 33 | } 34 | 35 | /** 36 | * Generate the SQL string for an DELETE command. 37 | */ 38 | protected String getQueryString() { 39 | StringBuffer sb = new StringBuffer(); 40 | sb.append("DELETE FROM "); 41 | sb.append(getTableName()); 42 | sb.append(getWhere()); 43 | return sb.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLInsert.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.sql.*; 4 | 5 | import ca.spaz.util.Logger; 6 | 7 | /** 8 | * Simplifies constructing SQL Insert Queries. 9 | * 10 | * @author davidson 11 | */ 12 | public class SQLInsert extends SQLStatement implements Columns { 13 | 14 | private SQLColumnSet cols; 15 | 16 | public SQLInsert(String tableName) { 17 | super(tableName, true, true); 18 | cols = new SQLColumnSet(); 19 | } 20 | 21 | protected void doExecute(Connection con) throws SQLException { 22 | Statement stmt = con.createStatement(); 23 | String query = this.getQueryString(); 24 | if (Logger.isDebugEnabled()) { 25 | Logger.debug("executeQuery() - Statement to be executed: " + query); 26 | } 27 | 28 | stmt.execute(query); 29 | } 30 | 31 | protected ResultSet doExecuteQuery(Connection con) throws SQLException { 32 | Statement stmt = con.createStatement(); 33 | String query = this.getQueryString(); 34 | if (Logger.isDebugEnabled()) { 35 | Logger.debug("executeQuery() - Statement to be executed: " + query); 36 | } 37 | 38 | stmt.executeUpdate(query); 39 | if (con.getMetaData().supportsGetGeneratedKeys()) { 40 | return stmt.getGeneratedKeys(); 41 | } else { 42 | return null; 43 | } 44 | } 45 | 46 | protected String getQueryString() { 47 | StringBuffer sb = new StringBuffer(); 48 | sb.append("INSERT INTO "); 49 | sb.append(this.table); 50 | 51 | sb.append(cols.getNameString()); 52 | 53 | sb.append("\n VALUES "); 54 | sb.append(cols.getValueString()); 55 | sb.append(";"); 56 | return sb.toString(); 57 | } 58 | 59 | /** 60 | * Retrieve the cols from the SQLInsert 61 | * @return Returns the cols. 62 | */ 63 | public SQLColumnSet getColumns() { 64 | return cols; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLRow.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.sql.*; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | 7 | /** 8 | * @TODO: rename, enhance. 9 | * 10 | * A super-simple relational-object mapping system. 11 | * 12 | * @author adavidson 13 | */ 14 | public class SQLRow { 15 | private String name; 16 | private HashMap rows = new HashMap(); 17 | 18 | public class SQLCol { 19 | int type; 20 | String name; 21 | Object value; 22 | } 23 | 24 | public SQLRow(String name) { 25 | this.name = name; 26 | } 27 | 28 | public void addColumn(String name, int type) { 29 | SQLCol col = new SQLCol(); 30 | col.type = type; 31 | col.name = name; 32 | rows.put(name, col); 33 | } 34 | 35 | public void setValue(String name, Object val) { 36 | SQLCol col = (SQLCol)rows.get(name); 37 | assert (col != null); 38 | if (col != null) { 39 | col.value = val; 40 | } 41 | } 42 | 43 | private String getDatabaseTypeName(int colType) { 44 | switch (colType) { 45 | case Types.TINYINT: { 46 | return "TINYINT"; 47 | } 48 | case Types.SMALLINT: { 49 | return "SMALLINT"; 50 | } 51 | case Types.INTEGER: { 52 | return "INTEGER"; 53 | } 54 | case Types.BIGINT: { 55 | return "BIGINT"; 56 | } 57 | case Types.VARCHAR: { 58 | return "VARCHAR"; 59 | } 60 | case Types.TIMESTAMP: { 61 | return "TIMESTAMP"; 62 | } 63 | case Types.DOUBLE: { 64 | return "DOUBLE"; 65 | } 66 | default: { 67 | return null; 68 | } 69 | } 70 | } 71 | 72 | public void createTable(Connection conn) throws SQLException { 73 | StringBuffer sql = new StringBuffer(); 74 | 75 | sql.append("CREATE TABLE "); 76 | sql.append(name); 77 | sql.append(" ( "); 78 | 79 | int count = 0; 80 | Iterator iter = rows.values().iterator(); 81 | while (iter.hasNext()) { 82 | SQLCol col = (SQLCol) iter.next(); 83 | sql.append(col.name); 84 | sql.append(" "); 85 | sql.append(getDatabaseTypeName(col.type)); 86 | if (++count < rows.size()) { 87 | sql.append(", "); 88 | } 89 | } 90 | sql.append(" ); "); 91 | 92 | conn.createStatement().execute(sql.toString()); 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLStatement.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.sql.*; 4 | 5 | /** 6 | * Base class for SQL command generators 7 | * 8 | * @author davidson 9 | */ 10 | public abstract class SQLStatement { 11 | 12 | public final void execute(Connection con) throws SQLException { 13 | if (!isExecuteSupported()) { 14 | throw new UnsupportedOperationException("Execute not supported on " 15 | + getClass().getName()); 16 | } 17 | doExecute(con); 18 | } 19 | 20 | protected void doExecute(Connection con) throws SQLException { 21 | } 22 | 23 | public final boolean isExecuteSupported() { 24 | return executeSupport; 25 | } 26 | 27 | public final ResultSet executeQuery(Connection con) throws SQLException { 28 | if (!isQuerySupported()) { 29 | throw new UnsupportedOperationException("Query not supported on " 30 | + getClass().getName()); 31 | } 32 | return doExecuteQuery(con); 33 | } 34 | 35 | protected ResultSet doExecuteQuery(Connection con) throws SQLException { 36 | return null; 37 | } 38 | 39 | public final boolean isQuerySupported() { 40 | return querySupport; 41 | } 42 | 43 | protected String table; 44 | 45 | private boolean querySupport; 46 | 47 | private boolean executeSupport; 48 | 49 | protected SQLStatement(String table, boolean querySupport, 50 | boolean executeSupport) { 51 | this.table = table; 52 | this.querySupport = querySupport; 53 | this.executeSupport = executeSupport; 54 | } 55 | 56 | public String getTableName() { 57 | return table; 58 | } 59 | 60 | public static String escape(String s) { 61 | return s.replaceAll("\\'", "\\'\\'"); 62 | } 63 | 64 | protected abstract String getQueryString(); 65 | 66 | public String toString() { 67 | return getQueryString(); 68 | } 69 | 70 | /** 71 | * Ensure that correct classes (with respect to toString()) ge 72 | * passed on 73 | * @param o the class to check. 74 | * @return a valid class for SQL 75 | */ 76 | static Object fixClass(Object o) { 77 | if (o instanceof java.util.Date) { 78 | o = new java.sql.Date(((java.util.Date)o).getTime()); 79 | } 80 | return o; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/ca/spaz/sql/SQLUpdate.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.sql; 2 | 3 | import java.sql.*; 4 | import java.util.List; 5 | 6 | import ca.spaz.util.Logger; 7 | 8 | /** 9 | * Simplifies constructing SQL Update Queries. 10 | */ 11 | public class SQLUpdate extends SQLSelectableStatement implements Columns { 12 | 13 | private SQLColumnSet cols; 14 | 15 | /** 16 | * Create a new SQLUpdate command for the given table 17 | * @param tableName the name of the table to update on 18 | */ 19 | public SQLUpdate(String tableName) { 20 | super(tableName, true, false, true); 21 | cols = new SQLColumnSet(); 22 | } 23 | 24 | /** 25 | * Overrides execute() and calls executeUpdate() 26 | */ 27 | protected void doExecute(Connection con) throws SQLException { 28 | Statement stmt = con.createStatement(); 29 | String query = this.getQueryString(); 30 | if (Logger.isDebugEnabled()) { 31 | Logger.debug("executeQuery() - Statement to be executed: " + query); 32 | } 33 | 34 | stmt.executeUpdate(query); 35 | } 36 | 37 | /** 38 | * Generate the SQL string for an UPDATE command. 39 | */ 40 | protected String getQueryString() { 41 | StringBuffer sb = new StringBuffer(); 42 | sb.append("UPDATE "); 43 | sb.append(getTableName()); 44 | sb.append(" SET "); 45 | List names = cols.getNames(); 46 | List terms = cols.getValues(); 47 | for (int i = 0; i < names.size(); i++) { 48 | Object name = names.get(i); 49 | Object term = terms.get(i); 50 | if (term == null) { 51 | term = "NULL"; 52 | } 53 | sb.append(name.toString()); 54 | sb.append(" = '"); 55 | sb.append(escape(term.toString())); 56 | sb.append("' "); 57 | if (i < names.size() - 1) { 58 | sb.append(", "); 59 | } 60 | } 61 | sb.append(getWhere()); 62 | return sb.toString(); 63 | } 64 | 65 | /** 66 | * Retrieve the cols from the SQLInsert 67 | * @return Returns the cols. 68 | */ 69 | public SQLColumnSet getColumns() { 70 | return cols; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ca/spaz/task/Task.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.task; 2 | 3 | public interface Task extends Runnable { 4 | 5 | /** 6 | * A number between 0 and 100 representing the 7 | * % completion of the task. 8 | * 9 | * @return the percentage complete 10 | */ 11 | public int getTaskProgress(); 12 | 13 | /** 14 | * The task has been cancelled. Attempt to stop. 15 | */ 16 | public void abortTask(); 17 | 18 | /** 19 | * Return true if the program can finish. 20 | * 21 | * @return true if the task can be aborted. 22 | */ 23 | public boolean canAbortTask(); 24 | 25 | /** 26 | * A description of the current Task being performed 27 | * 28 | * @return the description of the current Task being performed 29 | */ 30 | public String getTaskDescription(); 31 | } 32 | -------------------------------------------------------------------------------- /src/ca/spaz/task/TaskListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.task; 2 | 3 | public interface TaskListener { 4 | 5 | public void taskStarted(Task t); 6 | public void taskFinished(Task t); 7 | public void taskAborted(Task t); 8 | } 9 | -------------------------------------------------------------------------------- /src/ca/spaz/util/CountableInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. CountableBufferedInputStream and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.util; 13 | 14 | import java.io.*; 15 | 16 | /** 17 | * This class implements an InputStream that counts the bytes that it has 18 | * read. 19 | * 20 | * @author Chris Rose 21 | */ 22 | public class CountableInputStream extends FilterInputStream { 23 | 24 | private long bytesRetrieved = 0; 25 | 26 | private long markpos = -1; 27 | 28 | /** 29 | * Create a new CountableInputStream and wrap it around the 30 | * supplied InputStream. 31 | * 32 | * @param in an InputStream object to count. 33 | */ 34 | public CountableInputStream(InputStream in) { 35 | super(in); 36 | } 37 | 38 | /* (non-Javadoc) 39 | * @see java.io.InputStream#read() 40 | */ 41 | public synchronized int read() throws IOException { 42 | int next = super.read(); 43 | if (next != -1) { 44 | bytesRetrieved++; 45 | } 46 | return next; 47 | } 48 | 49 | /* (non-Javadoc) 50 | * @see java.io.InputStream#read(byte[], int, int) 51 | */ 52 | public synchronized int read(byte[] b, int off, int len) throws IOException { 53 | int next = super.read(b, off, len); 54 | if (next != -1) { 55 | bytesRetrieved += next; 56 | } 57 | return next; 58 | } 59 | 60 | /* (non-Javadoc) 61 | * @see java.io.InputStream#mark(int) 62 | */ 63 | public synchronized void mark(int readlimit) { 64 | super.mark(readlimit); 65 | markpos = bytesRetrieved; 66 | } 67 | 68 | /* (non-Javadoc) 69 | * @see java.io.InputStream#skip(long) 70 | */ 71 | public long skip(long n) throws IOException { 72 | long next = super.skip(n); 73 | bytesRetrieved += next; 74 | return next; 75 | } 76 | 77 | /* (non-Javadoc) 78 | * @see java.io.InputStream#reset() 79 | */ 80 | public synchronized void reset() throws IOException { 81 | bytesRetrieved = markpos; 82 | super.reset(); 83 | } 84 | 85 | /* (non-Javadoc) 86 | * @see java.io.InputStream#read(byte[]) 87 | */ 88 | public int read(byte[] b) throws IOException { 89 | int next = super.read(b); 90 | if (next != -1) { 91 | bytesRetrieved += next; 92 | } 93 | return next; 94 | } 95 | 96 | /** 97 | * Get a count of the bytes read by this stream. 98 | * @return the number of bytes read by this stream. 99 | */ 100 | public long getBytesRead() { 101 | return bytesRetrieved; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ca/spaz/util/ImageFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. ImageFactory and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.util; 13 | 14 | import java.awt.Image; 15 | import java.awt.Toolkit; 16 | import java.net.URL; 17 | import java.util.Map; 18 | 19 | /** 20 | * A class for manufacturing images from the resource path. 21 | * @author Chris Rose 22 | */ 23 | public class ImageFactory { 24 | 25 | private static ImageFactory instance = null; 26 | private int cacheSize; 27 | private Map imageCache; 28 | 29 | private ImageFactory(int cacheSz) { 30 | this.cacheSize = cacheSz; 31 | this.imageCache = new CacheMap(cacheSize); 32 | } 33 | 34 | public static final ImageFactory getInstance() { 35 | if (null == instance) { 36 | instance = new ImageFactory(10); 37 | } 38 | return instance; 39 | } 40 | 41 | public Image loadImage(URL url) { 42 | Image ret = null; 43 | if (imageCache.containsKey(url)) { 44 | ret = (Image) imageCache.get(url); 45 | } else { 46 | ret = Toolkit.getDefaultToolkit().createImage(url); 47 | if (ret != null) { 48 | imageCache.put(url, ret); 49 | } 50 | } 51 | return ret; 52 | } 53 | 54 | public Image loadImage(String resourceID) { 55 | return loadImage(resourceID, this); 56 | } 57 | 58 | public Image loadImage(String resourceID, Object source) { 59 | Class base = null; 60 | if (null == source) { 61 | base = this.getClass(); 62 | } else { 63 | base = source.getClass(); 64 | } 65 | URL url = base.getResource(resourceID); 66 | Image ret = null; 67 | if (url == null) { 68 | url = this.getClass().getResource(resourceID); 69 | if (url == null) { 70 | throw new IllegalArgumentException(resourceID + " is not a valid resource identifier"); 71 | } 72 | } 73 | ret = loadImage(url); 74 | return ret; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ca/spaz/util/JarLoader.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.*; 6 | 7 | /** 8 | * Allows class resources to be accessed by URL stream type. 9 | */ 10 | public class JarLoader extends URLStreamHandler implements URLStreamHandlerFactory { 11 | 12 | public URLStreamHandler createURLStreamHandler(String protocol) { 13 | return (protocol.equalsIgnoreCase("class")) ? this : null; 14 | } 15 | 16 | protected URLConnection openConnection(final URL url) throws IOException { 17 | return new URLConnection(url) { 18 | private Class base; 19 | 20 | public synchronized void connect() throws IOException { 21 | if (base == null) { 22 | try { 23 | base = Class.forName(url.getHost()); 24 | } catch (ClassNotFoundException e) { 25 | throw new IOException(e.getLocalizedMessage()); 26 | } 27 | } 28 | } 29 | 30 | public synchronized InputStream getInputStream() throws IOException { 31 | connect(); // make sure we're connected 32 | String path = url.getPath(); 33 | if (path.startsWith("/")) { 34 | path = path.substring(1); 35 | } 36 | return base.getClassLoader().getResourceAsStream(path); 37 | } 38 | 39 | public String getContentType() { 40 | return guessContentTypeFromName(url.getPath()); 41 | } 42 | 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ca/spaz/util/Logger.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.util; 2 | 3 | public class Logger { 4 | 5 | public static void log(String str) { 6 | System.out.println(str); 7 | } 8 | 9 | public static void debug(String str) { 10 | System.out.println(str); 11 | } 12 | 13 | public static void error(Exception e) { 14 | e.printStackTrace(); 15 | } 16 | 17 | public static void debug(Exception e) { 18 | e.printStackTrace(); 19 | } 20 | 21 | public static void error(String str) { 22 | System.err.println(str); 23 | } 24 | 25 | public static void error(String str, Exception e) { 26 | System.err.println(str); 27 | e.printStackTrace(); 28 | } 29 | 30 | public static boolean isDebugEnabled() { 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ca/spaz/util/ProgressListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | ******************************************************************************* 3 | * Copyright (c) 2005 Chris Rose and AIMedia 4 | * All rights reserved. ProgressListener and the accompanying materials 5 | * are made available under the terms of the Common Public License v1.0 6 | * which accompanies this distribution, and is available at 7 | * http://www.eclipse.org/legal/cpl-v10.html 8 | * 9 | * Contributors: 10 | * Chris Rose 11 | *******************************************************************************/ 12 | package ca.spaz.util; 13 | 14 | /** 15 | * This interface should be implemented by classes interested in being informed of the 16 | * progress of some other thread. 17 | * 18 | * @author Chris Rose 19 | */ 20 | public interface ProgressListener { 21 | 22 | /** 23 | * This event is fired when the progress item being tracked starts. 24 | */ 25 | void progressStart(); 26 | 27 | /** 28 | * This event is fired when the progress item being tracked finishes. At this point, 29 | * the item's progress should be considered to be 100% 30 | */ 31 | void progressFinish(); 32 | 33 | /** 34 | * This event is fired whenever the progress item being tracked updates its public 35 | * progress level. 36 | * 37 | * @param percent A number from 0 to 100 indicating the progress of the tracked item. 38 | */ 39 | void progress(int percent); 40 | } 41 | -------------------------------------------------------------------------------- /src/ca/spaz/util/SettingsChangeEvent.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.util; 2 | 3 | import java.util.EventObject; 4 | 5 | public class SettingsChangeEvent extends EventObject { 6 | 7 | private String key; 8 | private String newValue; 9 | 10 | public SettingsChangeEvent(Object source, String key, String val) { 11 | super(source); 12 | this.key = key; 13 | this.newValue = val; 14 | } 15 | 16 | /** 17 | * Returns the key of the setting that was changed. 18 | * 19 | * @return The key of the setting that was changed. 20 | */ 21 | public String getKey() { 22 | return key; 23 | } 24 | 25 | /** 26 | * Returns the new value for the setting. 27 | * 28 | * @return The new value for the setting, or null if the 29 | * setting was removed. 30 | */ 31 | public String getNewValue() { 32 | return newValue; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ca/spaz/util/SettingsChangeListener.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.util; 2 | 3 | public interface SettingsChangeListener extends java.util.EventListener { 4 | /** 5 | * This method gets called when a setting is added, removed or when 6 | * its value is changed. 7 | *

8 | * @param evt A SettingsChangeEvent object describing the event source 9 | * and the setting that has changed. 10 | */ 11 | void settingChange(SettingsChangeEvent evt); 12 | } 13 | -------------------------------------------------------------------------------- /src/ca/spaz/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.util; 2 | 3 | public class StringUtil { 4 | public static final int PAD_LEFT = 0; 5 | public static final int PAD_RIGHT = 1; 6 | public static final int PAD_CENTER = 2; 7 | 8 | public static String padr(String str, int num) { 9 | return pad(str, num, PAD_RIGHT, ' '); 10 | } 11 | 12 | public static String padl(String str, int num) { 13 | return pad(str, num, PAD_LEFT, ' '); 14 | } 15 | 16 | public static String padc(String str, int num) { 17 | return pad(str, num, PAD_CENTER, ' '); 18 | } 19 | 20 | public static String pad(String str, int num, int side, char pad) { 21 | int len = str.length(); 22 | if (len >= num) { 23 | return str; 24 | } 25 | StringBuffer sb = new StringBuffer(); 26 | switch (side) { 27 | case PAD_LEFT: 28 | while (len++ < num) { 29 | sb.append(pad); 30 | } 31 | sb.append(str); 32 | break; 33 | case PAD_RIGHT: 34 | sb.append(str); 35 | while (len++ < num) { 36 | sb.append(pad); 37 | } 38 | break; 39 | case PAD_CENTER: 40 | int n = (num - len) / 2; 41 | while (n-- >= 0) { 42 | sb.append(pad); 43 | } 44 | sb.append(str); 45 | while (len++ < num) { 46 | sb.append(pad); 47 | } 48 | break; 49 | } 50 | return sb.toString(); 51 | } 52 | 53 | public static String charRun(char c, int i) { 54 | StringBuffer sb = new StringBuffer(); 55 | while (i-- >= 0) { 56 | sb.append(c); 57 | } 58 | return sb.toString(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ca/spaz/wizard/WizardPanel.java: -------------------------------------------------------------------------------- 1 | package ca.spaz.wizard; 2 | 3 | import javax.swing.JPanel; 4 | 5 | public abstract class WizardPanel extends JPanel { 6 | private Wizard controller; 7 | 8 | public abstract String getWizardPanelTitle(); 9 | 10 | public abstract void commitChanges(); 11 | 12 | /** 13 | * @return true if the wizard panel is completed and user input is valid 14 | * The Wizard will not allow proceeding to next pane unless the current 15 | * pane returns isValid. 16 | */ 17 | public abstract boolean isValid(); 18 | 19 | 20 | protected void setWizard(Wizard w) { 21 | this.controller = w; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/com/apple/mrj/MRJAboutHandler.java: -------------------------------------------------------------------------------- 1 | package com.apple.mrj; 2 | /** 3 | * A stub for the windows build. This class is loaded under windows 4 | * and apple's real class is loaded instead on Mac OS X. 5 | * 6 | * @author Aaron Davidson 7 | */ 8 | public interface MRJAboutHandler { 9 | public void handleAbout(); 10 | } 11 | -------------------------------------------------------------------------------- /src/com/apple/mrj/MRJQuitHandler.java: -------------------------------------------------------------------------------- 1 | package com.apple.mrj; 2 | /** 3 | * A stub for the windows build. This class is loaded under windows 4 | * and apple's real class is loaded instead on Mac OS X. 5 | * 6 | * @author Aaron Davidson 7 | */ 8 | public interface MRJQuitHandler { 9 | public void handleQuit(); 10 | } 11 | -------------------------------------------------------------------------------- /src/docs/issues.html: -------------------------------------------------------------------------------- 1 | This is a scratch page right now for important nuances and issues that should be discussed in the user manual. 2 | 3 | ---- 4 | 5 | 6 | Not all nutrients in database are 'complete'. Some nutrients are under-represented in the 7 | USDA data-set, so it may appear as if you are deficient in those particular nutrients when 8 | the case is that any foods are simply lacking any information for those nutrients. 9 | 10 | * Am I deficient or is the Database? 11 | 12 | Vitamin D, for instance, is heavily underrepresented. It's high in many fishes like 13 | tuna and salmon, but most USDA entries on salmon and tuna lack information for Vitamin D. 14 | 15 | ---- 16 | 17 | Discuss all the hidden features like keyboard shortcuts, pop-up menus, etc... 18 | 19 | ---- 20 | 21 | Hidden feature: Multiple profiles 22 | 23 | If you pass it a single command line argument, it will create/load under a new account by that name. For example: 24 | 25 | ./Cronometer.exe Jon1 26 | 27 | Would load up the 'Jon1' profile instead of the default. 28 | 29 | Note: It doesn't currently show the name anywhere, so you'd have to handle that detail yourself for the time being. 30 | ---- 31 | 32 | USDA sr19 deprecated some foods from USDAsr18. The foods have been kept for 33 | backwards compatibility, but it is recommend you no longer use them. They will 34 | show up as light grey in the search and food listings. These items were removed 35 | because either the nutritional data is considered out of date and/or the product 36 | is no longer available on the market. 37 | 38 | 39 | ---- 40 | 41 | Scientific Tasks 42 | 43 | -- Read *carefully* through the USDA documentation and compile a list of important issues to consider 44 | -- find out which data may be unreliable and what nutrients are systematically underrepresented in the data 45 | 46 | -- Read carefully through DRIs for issues 47 | -- double check the values in nutrients.xml for mistakes 48 | -- add missing data 49 | -- double check algorithms and assumptions made in software 50 | -------------------------------------------------------------------------------- /src/docs/readme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

PLEASE READ CAREFULLY

4 | This tool helps you track your nutrition. Please understand that 5 | the data displayed in this software should always be treated as 6 | a rough estimate, not the gospel truth. The data itself may contain 7 | errors or be incomplete. The USDA food database, for instance, 8 | contains average values for most foods. Local soil and growing 9 | conditions can deviate largely from the average values. Some 10 | nutrients are under-represented in the database because not every 11 | food item has undergone comprehensive analysis. 12 | Furthermore, this software may contain unknown bugs that affect 13 | the reported values. Thus it is essential that you do not use this 14 | program to make life-critical decisions. 15 |

16 |
17 | The developers of this software accept no liability in the use of this 18 | software. 19 |
Use at your own risk.
20 | 21 |

"You get what you pay for"

22 | This software is provided free-of-charge, and is the result of countless 23 | hours of work by the developers. We continue to work on this software and 24 | support it at our own leisure. However, if you have an important 'must-have' 25 | feature, we will accept bribes. 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/fontawesome.tff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/src/fontawesome.tff -------------------------------------------------------------------------------- /src/img/apple-100x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/src/img/apple-100x100.png -------------------------------------------------------------------------------- /src/img/apple-50x50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/src/img/apple-50x50.png -------------------------------------------------------------------------------- /src/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myint/cronometer/7b7c13d179687188ec5a4fcf6c68ba3669d48f15/src/img/icon.png -------------------------------------------------------------------------------- /src/menubar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/todos.txt: -------------------------------------------------------------------------------- 1 | In lieu of excessive bug-tracker usage, the old-fashioned todo list: 2 | ----------------------------------------------------------- 3 | 4 | - migrate to swingx components (calendar, panels, gradients, glass panes, animations) 5 | - jxsearchbar? 6 | - tony's help tweaks 7 | - DRI stuff to investigate.... 8 | 9 | 10 | FIXED 11 | - missing 'import' message 12 | - bug 1698061 (stack trace) 13 | 14 | 15 | 0.9.0 TODOS: 16 | - Favorites list! 17 | - Fix commit changes bug in editors (fixed by using JXTable allegedly...) 18 | - scan for bad values and correct them? 19 | - continue to optimize data entry 20 | - fix focus traversal issues 21 | - export daily food to CSV, text, blog, etc... 22 | - TULs for water + flouride 23 | - cut & copy should be added to pop-up menu on servings 24 | - auto-set measure to most frequent entry? 25 | - default serving option per food 26 | 27 | Future / Deferred TODOS: 28 | - general heavy refactoring and code cleanup + documentation 29 | - more interesting reports & printing 30 | - more interesting ratios on front nutrition summary (zinc:copper, w3:w6) 31 | - auto-updater 32 | - more robust / scalable data backend 33 | - spaz.ca account synch 34 | - flavinoids + CRDB, etc...? 35 | - exercise log 36 | http://prevention.sph.sc.edu/tools/compendium.htm 37 | -------------------------------------------------------------------------------- /start_cronometer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ---------------------------------------------------------------------------- 3 | # Description Start Cronometer 4 | # 5 | # Date 2017-feb-24 6 | # Author Dimitar Misev 7 | # ---------------------------------------------------------------------------- 8 | 9 | # script name 10 | PROG=$(basename $0) 11 | 12 | # determine script directory 13 | SOURCE="${BASH_SOURCE[0]}" 14 | while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done 15 | SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 16 | 17 | CLASSPATH="." 18 | for f in "$SCRIPT_DIR/lib"/*; do 19 | CLASSPATH="$CLASSPATH:$f" 20 | done 21 | 22 | java -cp $CLASSPATH ca.spaz.cron.Cronometer 23 | --------------------------------------------------------------------------------