aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-04-18 09:06:19 +0000
committerdam <dam@gudinoff>2022-04-18 09:06:19 +0000
commit79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch)
tree6e90c6601893895d7640f478f0cad402cc7590b4
parent697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff)
parentee31a9a3d387121030a5f4503adeac5816d7726f (diff)
downloadsurgery-log-79691f93bab7aa093bb606bfb80d2b4b236ee091.tar.zst
surgery-log-79691f93bab7aa093bb606bfb80d2b4b236ee091.zip
Merge godot branch.v1.0
-rwxr-xr-xAndroidManifest.xml30
-rw-r--r--debug.keystorebin0 -> 3488 bytes
-rw-r--r--default_env.tres7
-rw-r--r--export_presets.cfg271
-rw-r--r--fonts/B612-Regular.ttfbin0 -> 153192 bytes
-rw-r--r--fonts/B612Mono-Regular.ttfbin0 -> 136716 bytes
-rw-r--r--fonts/entypo-fontello.ttfbin0 -> 6560 bytes
-rw-r--r--fonts/font_icons.tres8
-rw-r--r--fonts/font_mono_regular.tres8
-rw-r--r--fonts/font_regular.tres8
-rwxr-xr-xgen/net/redroid/medlog/BuildConfig.java6
-rwxr-xr-xgen/net/redroid/medlog/R.java145
-rwxr-xr-xic_launcher-web.pngbin118840 -> 0 bytes
-rwxr-xr-xicons/icon.pngbin0 -> 43811 bytes
-rw-r--r--icons/icon.png.import35
-rw-r--r--icons/icon.xcfbin0 -> 151900 bytes
-rw-r--r--icons/icon_background.pngbin0 -> 37713 bytes
-rw-r--r--icons/icon_background.png.import35
-rw-r--r--icons/icon_foreground.pngbin0 -> 6327 bytes
-rw-r--r--icons/icon_foreground.png.import35
-rwxr-xr-xlibs/android-support-v4.jarbin627582 -> 0 bytes
-rwxr-xr-xlibs/javacsv.jarbin13582 -> 0 bytes
-rwxr-xr-xlint.xml3
-rw-r--r--logic/database.gd288
-rw-r--r--logic/database_entry.gd74
-rw-r--r--logic/file_picker.gd18
-rw-r--r--logic/menu.gd152
-rw-r--r--logic/stage.gd294
-rw-r--r--main.gd65
-rw-r--r--main.tscn277
-rwxr-xr-xproguard-project.txt20
-rw-r--r--project.godot123
-rwxr-xr-xproject.properties14
-rw-r--r--readme.md82
-rwxr-xr-xres/drawable-hdpi/ic_launcher.pngbin6369 -> 0 bytes
-rwxr-xr-xres/drawable-mdpi/ic_launcher.pngbin3108 -> 0 bytes
-rwxr-xr-xres/drawable-xhdpi/ic_launcher.pngbin9945 -> 0 bytes
-rwxr-xr-xres/drawable-xxhdpi/ic_launcher.pngbin18410 -> 0 bytes
-rwxr-xr-xres/layout/activity_main.xml23
-rwxr-xr-xres/layout/fragment_file_operations.xml31
-rwxr-xr-xres/layout/fragment_main_dummy.xml16
-rwxr-xr-xres/layout/fragment_new_entry.xml216
-rwxr-xr-xres/layout/fragment_view_data.xml24
-rwxr-xr-xres/layout/multiline_spinner_dropdown_item.xml28
-rwxr-xr-xres/layout/override_new_entry.xml95
-rwxr-xr-xres/menu/main.xml9
-rwxr-xr-xres/values-sw600dp/dimens.xml8
-rwxr-xr-xres/values-sw720dp-land/dimens.xml9
-rwxr-xr-xres/values-v11/styles.xml11
-rwxr-xr-xres/values-v14/styles.xml12
-rwxr-xr-xres/values/dimens.xml7
-rwxr-xr-xres/values/strings.xml38
-rwxr-xr-xres/values/styles.xml20
-rwxr-xr-xsrc/com/example/surgerylog/FileOperationsFragment.java185
-rwxr-xr-xsrc/com/example/surgerylog/ISpinnerRefresh.java9
-rwxr-xr-xsrc/com/example/surgerylog/MainActivity.java330
-rwxr-xr-xsrc/com/example/surgerylog/MedLogInfo.java51
-rwxr-xr-xsrc/com/example/surgerylog/MedLogRoot.java142
-rwxr-xr-xsrc/com/example/surgerylog/MedLogSubType.java87
-rwxr-xr-xsrc/com/example/surgerylog/MedLogType.java52
-rwxr-xr-xsrc/com/example/surgerylog/NewEntryFragment.java437
-rwxr-xr-xsrc/com/example/surgerylog/ViewDataFragment.java173
-rw-r--r--themes/dark.tres126
-rw-r--r--ui/date_picker/date_picker.gd64
-rw-r--r--ui/date_picker/date_picker.tscn189
-rw-r--r--ui/date_picker/value_picker.gd132
-rw-r--r--ui/dialog/dialog.gd79
-rw-r--r--ui/modal_popup/modal_popup.gd76
-rw-r--r--ui/modal_popup/modal_popup.tscn40
-rw-r--r--ui/option_set/option_set.gd44
-rw-r--r--ui/option_set/option_set.tscn34
-rw-r--r--ui/option_set/option_set_list.gd294
-rw-r--r--ui/option_set/option_set_list.tscn9
-rw-r--r--ui/pointer_input_sensor.gd117
-rw-r--r--ui/touch_item_list/touch_item_list.gd68
-rw-r--r--ui/touch_item_list/touch_item_list.tscn19
-rw-r--r--ui/touch_vertical_container/touch_vertical_container.gd118
-rw-r--r--ui/touch_vertical_container/touch_vertical_container.tscn19
78 files changed, 3208 insertions, 2231 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100755
index fd4ec79..0000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="net.redroid.medlog"
- android:versionCode="1"
- android:versionName="1.8" >
-
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="16" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.Holo.Light.NoActionBar">
- <!-- android:theme="@android:style/Theme.Holo.NoActionBar"> -->
- <!-- android:theme="@style/AppTheme" > -->
-
- <activity
- android:name="com.example.surgerylog.MainActivity"
- android:label="@string/app_name" android:screenOrientation="portrait">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
diff --git a/debug.keystore b/debug.keystore
new file mode 100644
index 0000000..7b937fa
--- /dev/null
+++ b/debug.keystore
Binary files differ
diff --git a/default_env.tres b/default_env.tres
new file mode 100644
index 0000000..20207a4
--- /dev/null
+++ b/default_env.tres
@@ -0,0 +1,7 @@
+[gd_resource type="Environment" load_steps=2 format=2]
+
+[sub_resource type="ProceduralSky" id=1]
+
+[resource]
+background_mode = 2
+background_sky = SubResource( 1 )
diff --git a/export_presets.cfg b/export_presets.cfg
new file mode 100644
index 0000000..62e8bcb
--- /dev/null
+++ b/export_presets.cfg
@@ -0,0 +1,271 @@
+[preset.0]
+
+name="android"
+platform="Android"
+runnable=true
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path="build/surgery_log.apk"
+script_export_mode=1
+script_encryption_key=""
+
+[preset.0.options]
+
+custom_template/debug=""
+custom_template/release=""
+custom_template/use_custom_build=false
+custom_template/export_format=0
+architectures/armeabi-v7a=true
+architectures/arm64-v8a=false
+architectures/x86=false
+architectures/x86_64=false
+keystore/debug="debug.keystore"
+keystore/debug_user="debug.user"
+keystore/debug_password="debug.password"
+keystore/release=""
+keystore/release_user=""
+keystore/release_password=""
+one_click_deploy/clear_previous_install=false
+version/code=1
+version/name="1.0"
+version/min_sdk=19
+version/target_sdk=30
+package/unique_name="com.gudinoff.$genname"
+package/name=""
+package/signed=true
+package/classify_as_game=false
+package/retain_data_on_uninstall=false
+package/exclude_from_recents=false
+launcher_icons/main_192x192="res://icons/icon.png"
+launcher_icons/adaptive_foreground_432x432="res://icons/icon_foreground.png"
+launcher_icons/adaptive_background_432x432="res://icons/icon_background.png"
+graphics/32_bits_framebuffer=true
+graphics/opengl_debug=false
+xr_features/xr_mode=0
+xr_features/hand_tracking=0
+xr_features/hand_tracking_frequency=0
+xr_features/passthrough=0
+screen/immersive_mode=false
+screen/support_small=true
+screen/support_normal=true
+screen/support_large=true
+screen/support_xlarge=true
+user_data_backup/allow=false
+command_line/extra_args=""
+apk_expansion/enable=false
+apk_expansion/SALT=""
+apk_expansion/public_key=""
+permissions/custom_permissions=PoolStringArray( )
+permissions/access_checkin_properties=false
+permissions/access_coarse_location=false
+permissions/access_fine_location=false
+permissions/access_location_extra_commands=false
+permissions/access_mock_location=false
+permissions/access_network_state=false
+permissions/access_surface_flinger=false
+permissions/access_wifi_state=false
+permissions/account_manager=false
+permissions/add_voicemail=false
+permissions/authenticate_accounts=false
+permissions/battery_stats=false
+permissions/bind_accessibility_service=false
+permissions/bind_appwidget=false
+permissions/bind_device_admin=false
+permissions/bind_input_method=false
+permissions/bind_nfc_service=false
+permissions/bind_notification_listener_service=false
+permissions/bind_print_service=false
+permissions/bind_remoteviews=false
+permissions/bind_text_service=false
+permissions/bind_vpn_service=false
+permissions/bind_wallpaper=false
+permissions/bluetooth=false
+permissions/bluetooth_admin=false
+permissions/bluetooth_privileged=false
+permissions/brick=false
+permissions/broadcast_package_removed=false
+permissions/broadcast_sms=false
+permissions/broadcast_sticky=false
+permissions/broadcast_wap_push=false
+permissions/call_phone=false
+permissions/call_privileged=false
+permissions/camera=false
+permissions/capture_audio_output=false
+permissions/capture_secure_video_output=false
+permissions/capture_video_output=false
+permissions/change_component_enabled_state=false
+permissions/change_configuration=false
+permissions/change_network_state=false
+permissions/change_wifi_multicast_state=false
+permissions/change_wifi_state=false
+permissions/clear_app_cache=false
+permissions/clear_app_user_data=false
+permissions/control_location_updates=false
+permissions/delete_cache_files=false
+permissions/delete_packages=false
+permissions/device_power=false
+permissions/diagnostic=false
+permissions/disable_keyguard=false
+permissions/dump=false
+permissions/expand_status_bar=false
+permissions/factory_test=false
+permissions/flashlight=false
+permissions/force_back=false
+permissions/get_accounts=false
+permissions/get_package_size=false
+permissions/get_tasks=false
+permissions/get_top_activity_info=false
+permissions/global_search=false
+permissions/hardware_test=false
+permissions/inject_events=false
+permissions/install_location_provider=false
+permissions/install_packages=false
+permissions/install_shortcut=false
+permissions/internal_system_window=false
+permissions/internet=false
+permissions/kill_background_processes=false
+permissions/location_hardware=false
+permissions/manage_accounts=false
+permissions/manage_app_tokens=false
+permissions/manage_documents=false
+permissions/master_clear=false
+permissions/media_content_control=false
+permissions/modify_audio_settings=false
+permissions/modify_phone_state=false
+permissions/mount_format_filesystems=false
+permissions/mount_unmount_filesystems=false
+permissions/nfc=false
+permissions/persistent_activity=false
+permissions/process_outgoing_calls=false
+permissions/read_calendar=false
+permissions/read_call_log=false
+permissions/read_contacts=false
+permissions/read_external_storage=true
+permissions/read_frame_buffer=false
+permissions/read_history_bookmarks=false
+permissions/read_input_state=false
+permissions/read_logs=false
+permissions/read_phone_state=false
+permissions/read_profile=false
+permissions/read_sms=false
+permissions/read_social_stream=false
+permissions/read_sync_settings=false
+permissions/read_sync_stats=false
+permissions/read_user_dictionary=false
+permissions/reboot=false
+permissions/receive_boot_completed=false
+permissions/receive_mms=false
+permissions/receive_sms=false
+permissions/receive_wap_push=false
+permissions/record_audio=false
+permissions/reorder_tasks=false
+permissions/restart_packages=false
+permissions/send_respond_via_message=false
+permissions/send_sms=false
+permissions/set_activity_watcher=false
+permissions/set_alarm=false
+permissions/set_always_finish=false
+permissions/set_animation_scale=false
+permissions/set_debug_app=false
+permissions/set_orientation=false
+permissions/set_pointer_speed=false
+permissions/set_preferred_applications=false
+permissions/set_process_limit=false
+permissions/set_time=false
+permissions/set_time_zone=false
+permissions/set_wallpaper=false
+permissions/set_wallpaper_hints=false
+permissions/signal_persistent_processes=false
+permissions/status_bar=false
+permissions/subscribed_feeds_read=false
+permissions/subscribed_feeds_write=false
+permissions/system_alert_window=false
+permissions/transmit_ir=false
+permissions/uninstall_shortcut=false
+permissions/update_device_stats=false
+permissions/use_credentials=false
+permissions/use_sip=false
+permissions/vibrate=false
+permissions/wake_lock=false
+permissions/write_apn_settings=false
+permissions/write_calendar=false
+permissions/write_call_log=false
+permissions/write_contacts=false
+permissions/write_external_storage=true
+permissions/write_gservices=false
+permissions/write_history_bookmarks=false
+permissions/write_profile=false
+permissions/write_secure_settings=false
+permissions/write_settings=false
+permissions/write_sms=false
+permissions/write_social_stream=false
+permissions/write_sync_settings=false
+permissions/write_user_dictionary=false
+
+[preset.1]
+
+name="linux"
+platform="Linux/X11"
+runnable=true
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path="build/surgery_log.x64"
+script_export_mode=1
+script_encryption_key=""
+
+[preset.1.options]
+
+custom_template/debug=""
+custom_template/release=""
+binary_format/64_bits=true
+binary_format/embed_pck=true
+texture_format/bptc=false
+texture_format/s3tc=true
+texture_format/etc=false
+texture_format/etc2=false
+texture_format/no_bptc_fallbacks=true
+
+[preset.2]
+
+name="windows"
+platform="Windows Desktop"
+runnable=true
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path="build/surgery_log.exe"
+script_export_mode=1
+script_encryption_key=""
+
+[preset.2.options]
+
+custom_template/debug=""
+custom_template/release=""
+binary_format/64_bits=true
+binary_format/embed_pck=true
+texture_format/bptc=false
+texture_format/s3tc=true
+texture_format/etc=false
+texture_format/etc2=false
+texture_format/no_bptc_fallbacks=true
+codesign/enable=false
+codesign/identity=""
+codesign/password=""
+codesign/timestamp=true
+codesign/timestamp_server_url=""
+codesign/digest_algorithm=1
+codesign/description=""
+codesign/custom_options=PoolStringArray( )
+application/icon="res://icons/icon.png"
+application/file_version=""
+application/product_version=""
+application/company_name=""
+application/product_name=""
+application/file_description=""
+application/copyright=""
+application/trademarks=""
diff --git a/fonts/B612-Regular.ttf b/fonts/B612-Regular.ttf
new file mode 100644
index 0000000..0574897
--- /dev/null
+++ b/fonts/B612-Regular.ttf
Binary files differ
diff --git a/fonts/B612Mono-Regular.ttf b/fonts/B612Mono-Regular.ttf
new file mode 100644
index 0000000..ec09c98
--- /dev/null
+++ b/fonts/B612Mono-Regular.ttf
Binary files differ
diff --git a/fonts/entypo-fontello.ttf b/fonts/entypo-fontello.ttf
new file mode 100644
index 0000000..345bd55
--- /dev/null
+++ b/fonts/entypo-fontello.ttf
Binary files differ
diff --git a/fonts/font_icons.tres b/fonts/font_icons.tres
new file mode 100644
index 0000000..6bc9f3d
--- /dev/null
+++ b/fonts/font_icons.tres
@@ -0,0 +1,8 @@
+[gd_resource type="DynamicFont" load_steps=2 format=2]
+
+[ext_resource path="res://fonts/entypo-fontello.ttf" type="DynamicFontData" id=1]
+
+[resource]
+size = 55
+use_filter = true
+font_data = ExtResource( 1 )
diff --git a/fonts/font_mono_regular.tres b/fonts/font_mono_regular.tres
new file mode 100644
index 0000000..d42e75b
--- /dev/null
+++ b/fonts/font_mono_regular.tres
@@ -0,0 +1,8 @@
+[gd_resource type="DynamicFont" load_steps=2 format=2]
+
+[ext_resource path="res://fonts/B612Mono-Regular.ttf" type="DynamicFontData" id=1]
+
+[resource]
+size = 42
+use_filter = true
+font_data = ExtResource( 1 )
diff --git a/fonts/font_regular.tres b/fonts/font_regular.tres
new file mode 100644
index 0000000..1090222
--- /dev/null
+++ b/fonts/font_regular.tres
@@ -0,0 +1,8 @@
+[gd_resource type="DynamicFont" load_steps=2 format=2]
+
+[ext_resource path="res://fonts/B612-Regular.ttf" type="DynamicFontData" id=1]
+
+[resource]
+size = 32
+use_filter = true
+font_data = ExtResource( 1 )
diff --git a/gen/net/redroid/medlog/BuildConfig.java b/gen/net/redroid/medlog/BuildConfig.java
deleted file mode 100755
index c27f499..0000000
--- a/gen/net/redroid/medlog/BuildConfig.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/** Automatically generated file. DO NOT MODIFY */
-package net.redroid.medlog;
-
-public final class BuildConfig {
- public final static boolean DEBUG = true;
-} \ No newline at end of file
diff --git a/gen/net/redroid/medlog/R.java b/gen/net/redroid/medlog/R.java
deleted file mode 100755
index 59722b1..0000000
--- a/gen/net/redroid/medlog/R.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/* AUTO-GENERATED FILE. DO NOT MODIFY.
- *
- * This class was automatically generated by the
- * aapt tool from the resource data it found. It
- * should not be modified by hand.
- */
-
-package net.redroid.medlog;
-
-public final class R {
- public static final class attr {
- }
- public static final class dimen {
- /** Default screen margins, per the Android Design guidelines.
-
- Customize dimensions originally defined in res/values/dimens.xml (such as
- screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
-
- */
- public static final int activity_horizontal_margin=0x7f040000;
- public static final int activity_vertical_margin=0x7f040001;
- }
- public static final class drawable {
- public static final int ic_launcher=0x7f020000;
- }
- public static final class id {
- public static final int FileOperationsView=0x7f080002;
- public static final int NewEntryView=0x7f080006;
- public static final int OverrideNewEntry=0x7f080022;
- public static final int RelativeLayout1=0x7f08001f;
- public static final int action_settings=0x7f08002b;
- public static final int anesthesiaOverride=0x7f08000d;
- public static final int anesthesiaSpinner=0x7f08000c;
- public static final int dataBaseListView=0x7f080021;
- public static final int datePicker=0x7f080008;
- public static final int exportDataBase=0x7f080004;
- public static final int fillOverrideButton=0x7f08001d;
- public static final int firstAiderOverride=0x7f08000f;
- public static final int firstAiderSpinner=0x7f08000e;
- public static final int importButton=0x7f080003;
- public static final int interventionOverride=0x7f080019;
- public static final int interventionSpinner=0x7f080018;
- public static final int medSubSubTypeOverride=0x7f080015;
- public static final int medSubSubTypeSpinner=0x7f080014;
- public static final int medSubTypeOverride=0x7f080013;
- public static final int medSubTypeSpinner=0x7f080012;
- public static final int medTypeOverride=0x7f080011;
- public static final int medTypeSpinner=0x7f080010;
- public static final int nProcess=0x7f080007;
- public static final int nSurgery=0x7f080009;
- public static final int notesText=0x7f08001b;
- public static final int overrideAnesthesia=0x7f08002a;
- public static final int overrideCheckBox=0x7f08001c;
- public static final int overrideFirstAider=0x7f080028;
- public static final int overrideIntervention=0x7f080027;
- public static final int overridePatholoty=0x7f080026;
- public static final int overridePlace=0x7f080029;
- public static final int overrideSubSubType=0x7f080025;
- public static final int overrideSubType=0x7f080024;
- public static final int overrideType=0x7f080023;
- public static final int pager=0x7f080000;
- public static final int pager_title_strip=0x7f080001;
- public static final int pathologyOverride=0x7f080017;
- public static final int pathologySpinner=0x7f080016;
- public static final int placeOverride=0x7f08000b;
- public static final int placeSpinner=0x7f08000a;
- public static final int refreshBtn=0x7f080020;
- public static final int saveButton=0x7f08001e;
- public static final int section_label=0x7f080005;
- public static final int urgencyCheckBox=0x7f08001a;
- }
- public static final class layout {
- public static final int activity_main=0x7f030000;
- public static final int fragment_file_operations=0x7f030001;
- public static final int fragment_main_dummy=0x7f030002;
- public static final int fragment_new_entry=0x7f030003;
- public static final int fragment_view_data=0x7f030004;
- public static final int multiline_spinner_dropdown_item=0x7f030005;
- public static final int override_new_entry=0x7f030006;
- }
- public static final class menu {
- public static final int main=0x7f070000;
- }
- public static final class string {
- public static final int action_settings=0x7f050001;
- public static final int anesthesia_str=0x7f05001c;
- public static final int app_name=0x7f050000;
- public static final int delete_str=0x7f05000c;
- public static final int errorMissingProcessNumber_str=0x7f050010;
- public static final int exportDataBase_str=0x7f050009;
- public static final int exportMessageError_str=0x7f05000f;
- public static final int exportMessage_str=0x7f05000e;
- public static final int filloverride_str=0x7f050015;
- public static final int firstaider_str=0x7f05001b;
- public static final int importConfigs_str=0x7f050008;
- public static final int intervention_str=0x7f05001a;
- public static final int nProcess_str=0x7f050005;
- public static final int nSurgery_str=0x7f050006;
- public static final int no_str=0x7f050013;
- public static final int notes_str=0x7f05001e;
- public static final int ok_str=0x7f05000b;
- public static final int override_str=0x7f050014;
- public static final int pathology_str=0x7f050019;
- public static final int place_str=0x7f05001d;
- public static final int refresh_str=0x7f05000d;
- public static final int save_str=0x7f05000a;
- public static final int subsubtype_str=0x7f050018;
- public static final int subtype_str=0x7f050017;
- public static final int successSavingEntry_str=0x7f050011;
- public static final int title_section1=0x7f050002;
- public static final int title_section2=0x7f050003;
- public static final int title_section3=0x7f050004;
- public static final int type_str=0x7f050016;
- public static final int urgency_str=0x7f050007;
- public static final int yes_str=0x7f050012;
- }
- public static final class style {
- /**
- Base application theme, dependent on API level. This theme is replaced
- by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-
-
- Theme customizations available in newer API levels can go in
- res/values-vXX/styles.xml, while customizations related to
- backward-compatibility can go here.
-
-
- Base application theme for API 11+. This theme completely replaces
- AppBaseTheme from res/values/styles.xml on API 11+ devices.
-
- API 11 theme customizations can go here.
-
- Base application theme for API 14+. This theme completely replaces
- AppBaseTheme from BOTH res/values/styles.xml and
- res/values-v11/styles.xml on API 14+ devices.
-
- API 14 theme customizations can go here.
- */
- public static final int AppBaseTheme=0x7f060000;
- /** Application theme.
- All customizations that are NOT specific to a particular API-level can go here.
- */
- public static final int AppTheme=0x7f060001;
- }
-}
diff --git a/ic_launcher-web.png b/ic_launcher-web.png
deleted file mode 100755
index e4d20ec..0000000
--- a/ic_launcher-web.png
+++ /dev/null
Binary files differ
diff --git a/icons/icon.png b/icons/icon.png
new file mode 100755
index 0000000..95c7db3
--- /dev/null
+++ b/icons/icon.png
Binary files differ
diff --git a/icons/icon.png.import b/icons/icon.png.import
new file mode 100644
index 0000000..bc6c6d2
--- /dev/null
+++ b/icons/icon.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/icon.png-66e56a551f174bdc993eb27d415a6710.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icons/icon.png"
+dest_files=[ "res://.import/icon.png-66e56a551f174bdc993eb27d415a6710.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/icons/icon.xcf b/icons/icon.xcf
new file mode 100644
index 0000000..57986fb
--- /dev/null
+++ b/icons/icon.xcf
Binary files differ
diff --git a/icons/icon_background.png b/icons/icon_background.png
new file mode 100644
index 0000000..bbb555d
--- /dev/null
+++ b/icons/icon_background.png
Binary files differ
diff --git a/icons/icon_background.png.import b/icons/icon_background.png.import
new file mode 100644
index 0000000..7b6caab
--- /dev/null
+++ b/icons/icon_background.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/icon_background.png-7c479113bc74649f6884d5867344a330.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icons/icon_background.png"
+dest_files=[ "res://.import/icon_background.png-7c479113bc74649f6884d5867344a330.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/icons/icon_foreground.png b/icons/icon_foreground.png
new file mode 100644
index 0000000..919f2ff
--- /dev/null
+++ b/icons/icon_foreground.png
Binary files differ
diff --git a/icons/icon_foreground.png.import b/icons/icon_foreground.png.import
new file mode 100644
index 0000000..8427899
--- /dev/null
+++ b/icons/icon_foreground.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/icon_foreground.png-386f0ed4375c2b690a85127a54f3c7b2.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icons/icon_foreground.png"
+dest_files=[ "res://.import/icon_foreground.png-386f0ed4375c2b690a85127a54f3c7b2.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
deleted file mode 100755
index 96644ed..0000000
--- a/libs/android-support-v4.jar
+++ /dev/null
Binary files differ
diff --git a/libs/javacsv.jar b/libs/javacsv.jar
deleted file mode 100755
index ceb59eb..0000000
--- a/libs/javacsv.jar
+++ /dev/null
Binary files differ
diff --git a/lint.xml b/lint.xml
deleted file mode 100755
index ee0eead..0000000
--- a/lint.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<lint>
-</lint> \ No newline at end of file
diff --git a/logic/database.gd b/logic/database.gd
new file mode 100644
index 0000000..ed80ae7
--- /dev/null
+++ b/logic/database.gd
@@ -0,0 +1,288 @@
+extends TouchItemList
+class_name Database
+
+const DATABASE_FILE_PATH: String = "user://database.json"
+const DATABASE_FILE_VERSION: String = "SL_DB_V1"
+
+var db: Array
+var selected_idx: int
+var staged_idx: int
+
+onready var stage := get_node("/root/main/stage") # as Stage # Commented to avoid cyclic dependencies.
+onready var delete_button := get_node("actions/delete") as Button
+onready var edit_button := get_node("actions/edit") as Button
+onready var add_button := get_node("actions/add") as Button
+onready var popup := get_node("/root/main/popup") as ModalPopup
+onready var dialog := get_node("/root/main/dialog") as Dialog
+
+func _init():
+ selected_idx = -1
+ staged_idx = -1
+
+
+func _ready():
+ self.connect("item_selected", self, "item_selected")
+ self.connect("nothing_selected", self, "clear_selection")
+
+ delete_button.connect("pressed", self, "delete_action")
+ edit_button.connect("pressed", self, "edit_action")
+ add_button.connect("pressed", self, "add_action")
+
+ stage.connect("save", self, "save_stage")
+ stage.connect("discard", self, "discard_stage")
+
+ load_database()
+
+
+func get_entry_view(database_entry: Dictionary) -> String:
+ return "%9s | %4s | %04d-%02d" % [database_entry.process_id, database_entry.surgery_id, database_entry.date_year, database_entry.date_month]
+
+
+func item_selected(index: int):
+ selected_idx = index
+ set_buttons_active(true)
+
+
+func clear_selection():
+ selected_idx = -1
+ unselect_all()
+ set_buttons_active(false)
+
+
+func set_buttons_active(active: bool):
+ (get_node("actions/delete") as Button).disabled = !active
+ (get_node("actions/edit") as Button).disabled = !active
+
+
+func delete_action():
+ if selected_idx < 0:
+ return
+
+ dialog.setup("The entry #%d with process ID '%s' will be deleted from the database." % [selected_idx+1, db[selected_idx].process_id], "Delete", "No")
+ dialog.connect("accepted", self, "delete_action_confirmed")
+ popup.open_popup("Delete entry?", dialog)
+
+
+func delete_action_confirmed():
+ db.remove(selected_idx)
+ self.remove_item(selected_idx)
+ selected_idx = -1
+ save_database()
+ clear_selection()
+
+
+func edit_action():
+ if selected_idx < 0:
+ return
+
+ staged_idx = selected_idx
+ self.visible = false
+ stage.visible = true
+ var staged := (db[staged_idx] as Dictionary).duplicate(true)
+ stage.set_stage(staged, "Entry #%d" % (staged_idx+1))
+
+
+func add_action():
+ self.visible = false
+ stage.visible = true
+ var staged := DatabaseEntry.instance_entry()
+ stage.set_stage(staged, "New entry")
+
+
+func save_stage(entry: Dictionary):
+ if DatabaseEntry.is_valid_entry(entry) == false:
+ printerr("Invalid entry detected.")
+ return
+
+ var next_selected_idx: int
+ if staged_idx >= 0:
+ db[staged_idx] = entry
+ next_selected_idx = staged_idx
+ else:
+ db.append(entry)
+ self.add_item(get_entry_view(entry))
+ next_selected_idx = db.size() - 1
+
+ select(next_selected_idx)
+ emit_signal("item_selected", next_selected_idx) # Calling "select" does not trigger the "item_selected" signal.
+ set_item_text(selected_idx, get_entry_view(db[selected_idx]))
+ ensure_current_is_visible()
+
+ save_database()
+
+ staged_idx = -1
+ self.visible = true
+ grab_focus()
+
+
+func discard_stage():
+ staged_idx = -1
+ self.visible = true
+ grab_focus()
+
+
+func save_database(file_path: String = DATABASE_FILE_PATH):
+ match file_path.get_extension():
+ "json":
+ export_json(file_path, db)
+
+ "csv":
+ export_csv(file_path, db)
+
+ _:
+ printerr("Invalid database file extension '%s', expected 'json' or 'csv'." % file_path.get_file())
+ return
+
+
+static func export_json(file_path: String, data: Array):
+ var database_file := {
+ "version": DATABASE_FILE_VERSION,
+ "database": data,
+ }
+ var indentation_char := "" if file_path == DATABASE_FILE_PATH else "\t"
+ var file_content := JSON.print(database_file, indentation_char)
+
+ var file := File.new()
+ file.open(file_path, File.WRITE)
+ file.store_string(file_content)
+ file.close()
+
+
+static func export_csv(file_path: String, data: Array):
+ var file := File.new()
+ file.open(file_path, File.WRITE)
+ var header := PoolStringArray(DatabaseEntry.ENTRY_PROTOTYPE.keys())
+ file.store_csv_line(header)
+ for it in data:
+ file.store_csv_line(it.values())
+ file.close()
+
+
+func load_database(file_path: String = DATABASE_FILE_PATH):
+ match file_path.get_extension():
+ "json":
+ clear_database()
+ db = import_json(file_path)
+ if file_path != DATABASE_FILE_PATH:
+ sanitize_database(db)
+
+ "csv":
+ clear_database()
+ db = import_csv(file_path)
+
+ _:
+ printerr("Invalid database file extension '%s', expected 'json' or 'csv'." % file_path.get_file())
+ return
+
+ for it in db:
+ # The JSON specification does not define integer or float types, but only a number type.
+ # Therefore, converting a Variant to JSON text will convert all numerical values to float types.
+ # Thus, we cast all integer values once we load them.
+ it["date_year"] = int(it["date_year"])
+ it["date_month"] = int(it["date_month"])
+ it["date_day"] = int(it["date_day"])
+
+ self.add_item(get_entry_view(it))
+
+
+static func sanitize_database(database: Array):
+ var blueprint := DatabaseEntry.ENTRY_PROTOTYPE
+
+ for entry in database:
+ # Delete extra keys.
+ var keys_to_delete: Array
+ for key in entry:
+ if blueprint.has(key) == false:
+ keys_to_delete.append(key)
+ for key in keys_to_delete:
+ entry.erase(key)
+
+ # Fix wrong value types.
+ for key in blueprint:
+ if entry.has(key) == false || typeof(entry[key]) != typeof(blueprint[key]):
+ entry[key] = blueprint[key]
+
+
+static func import_json(file_path: String) -> Array:
+ var result := []
+
+ var file := File.new()
+ var error := file.open(file_path, File.READ_WRITE)
+ if error != OK:
+ printerr("Failed to open database file '%s' (error %d)." % [file_path, error])
+ return result
+ var file_content = file.get_as_text()
+ file.close()
+ var parse_result = JSON.parse(file_content)
+
+ if parse_result.error != OK:
+ printerr("Failed to parse database file '%s' (error %d)." % [file_path, parse_result.error])
+ return result
+
+ if typeof(parse_result.result) != TYPE_DICTIONARY:
+ printerr("Invalid database file type '%s', expected '%s'." % [typeof(parse_result.result), TYPE_DICTIONARY])
+ return result
+
+ if parse_result.result["version"] != DATABASE_FILE_VERSION:
+ printerr("Invalid database file version '%s', expected '%s'." % [parse_result.result["version"], DATABASE_FILE_VERSION])
+ return result
+
+ if typeof(parse_result.result["database"]) != TYPE_ARRAY:
+ printerr("Invalid database content type '%s', expected '%s'." % [typeof(parse_result.result["database"]), TYPE_ARRAY])
+ return result
+
+ result = parse_result.result["database"]
+ return result
+
+
+static func import_csv(file_path: String) -> Array:
+ var result: Array
+ var file := File.new()
+ file.open(file_path, File.READ_WRITE)
+ var headers := file.get_csv_line()
+ while file.get_position() < file.get_len():
+ var csv_entry := file.get_csv_line()
+ var entry = DatabaseEntry.instance_entry()
+ for idx in headers.size():
+ var field_name := headers[idx]
+ var field_value := csv_entry[idx]
+ match field_name:
+ "date":
+ DatabaseEntry.set_entry_date(entry, field_value)
+ "date_year", "date_month", "date_day":
+ entry[field_name] = int(field_value)
+ "is_urgency":
+ entry[field_name] = true if field_value.strip_edges().to_lower() == "true" else false
+ _:
+ if DatabaseEntry.ENTRY_PROTOTYPE.has(field_name):
+ entry[field_name] = field_value
+ if DatabaseEntry.is_valid_entry(entry):
+ result.append(entry)
+ file.close()
+ return result
+
+
+func clear_database():
+ clear_selection()
+ self.clear()
+ db.resize(0)
+
+
+#func DEBUG_create_fake_database():
+# clear_database()
+# for idx in range(100):
+# var today := OS.get_date(true)
+# var date_year = today.year + int(float(idx) / 30.0 / 12)
+# var date_month = 1 + int(float(idx) / 30.0) % 12
+# var date_day = 1 + (idx % 30)
+# var fake_entry = DatabaseEntry.instance_entry({
+# "process_id": "%06d" % idx,
+# "surgery_id": "s%05d" % idx,
+# "date_year": date_year,
+# "date_month": date_month,
+# "date_day": date_day,
+# })
+# db.append(fake_entry)
+# self.add_item(get_entry_view(fake_entry))
+
+
diff --git a/logic/database_entry.gd b/logic/database_entry.gd
new file mode 100644
index 0000000..8b0c51f
--- /dev/null
+++ b/logic/database_entry.gd
@@ -0,0 +1,74 @@
+extends Reference
+class_name DatabaseEntry
+
+const DATE_SEPARATOR: String = "-"
+const DATE_FORMAT: String = "%04d-%02d-%02d"
+const ENTRY_PROTOTYPE: Dictionary = {
+ "process_id": "",
+ "surgery_id": "",
+ "date_year": 1,
+ "date_month": 1,
+ "date_day": 1,
+ "place": "",
+ "anesthesia": "",
+ "first_assistant": "",
+ "type": "",
+ "sub_type": "",
+ "sub_sub_type": "",
+ "pathology": "",
+ "intervention": "",
+ "is_urgency": false,
+ "notes": "",
+}
+
+
+static func instance_entry(params: Dictionary = {}) -> Dictionary:
+ var new_entry := ENTRY_PROTOTYPE.duplicate(true)
+ new_entry.process_id = params.get("process_id", "")
+ new_entry.surgery_id = params.get("surgery_id", "")
+
+ var today = OS.get_date()
+ new_entry.date_year = params.get("date_year", today.year)
+ new_entry.date_month = params.get("date_month", today.month)
+ new_entry.date_day = params.get("date_day", today.day)
+
+ new_entry.place = params.get("place", "")
+ new_entry.anesthesia = params.get("anesthesia", "")
+ new_entry.first_assistant = params.get("first_assistant", "")
+ new_entry.type = params.get("type", "")
+ new_entry.sub_type = params.get("sub_type", "")
+ new_entry.sub_sub_type = params.get("sub_sub_type", "")
+ new_entry.pathology = params.get("pathology", "")
+ new_entry.intervention = params.get("intervention", "")
+ new_entry.is_urgency = params.get("is_urgency", false)
+ new_entry.notes = params.get("notes", "")
+
+ return new_entry
+
+
+static func is_valid_entry(entry: Dictionary) -> bool:
+ var is_valid: bool
+
+ is_valid = entry.has_all(ENTRY_PROTOTYPE.keys()) && ENTRY_PROTOTYPE.keys().size() == entry.keys().size()
+
+ for it in ENTRY_PROTOTYPE.keys():
+ if typeof(ENTRY_PROTOTYPE[it]) != typeof(entry[it]):
+ is_valid = false
+ break
+
+ return is_valid
+
+
+static func get_entry_date(entry: Dictionary) -> String:
+ return DATE_FORMAT % [entry.date_year, entry.date_month, entry.date_day]
+
+
+static func set_entry_date(entry: Dictionary, date: String):
+ date = date.strip_edges().replace(" ", DATE_SEPARATOR).replace("/", DATE_SEPARATOR).replace("\\", DATE_SEPARATOR)
+ var year_month_idx := date.find(DATE_SEPARATOR)
+ var month_day_idx := date.find(DATE_SEPARATOR, year_month_idx + 1)
+ entry.date_year = int(date.substr(0, year_month_idx))
+ entry.date_month = int(date.substr(year_month_idx + 1, month_day_idx - year_month_idx - 1))
+ entry.date_day = int(date.substr(month_day_idx + 1))
+
+
diff --git a/logic/file_picker.gd b/logic/file_picker.gd
new file mode 100644
index 0000000..9715d0d
--- /dev/null
+++ b/logic/file_picker.gd
@@ -0,0 +1,18 @@
+extends FileDialog
+
+export var clear_signals_on_hide := true
+
+
+func _init():
+ self.connect("hide", self, "_clear_signals")
+
+
+func _clear_signals():
+ if clear_signals_on_hide == false:
+ return
+
+ for signal_name in ["file_selected"]:
+ for it in get_signal_connection_list(signal_name):
+ disconnect(it.signal, it.target, it.method)
+
+
diff --git a/logic/menu.gd b/logic/menu.gd
new file mode 100644
index 0000000..4b0b7b3
--- /dev/null
+++ b/logic/menu.gd
@@ -0,0 +1,152 @@
+extends MenuButton
+
+const LOGS_FILE_PATH: String = "user://logs/godot.log"
+const menu_items: Array = [
+ { label = "Import Option Sets", action = "import_option_sets_action" },
+ { label = "Export Option Sets", action = "export_option_sets_action" },
+ { label = "Clear Option Sets", action = "clear_option_sets_action" },
+ { label = "Import Database", action = "import_database_action" },
+ { label = "Export Database", action = "export_database_action" },
+ { label = "Clear Database", action = "clear_database_action" },
+ { label = "Export App Log", action = "export_app_log_action" },
+ { label = "About", action = "about_action" },
+]
+const license_font_b612: String = "res://licenses/font_b612.txt"
+const license_godot: String = "res://licenses/godot.txt"
+
+onready var menu := get_popup() as PopupMenu
+onready var popup := get_node("/root/main/popup") as ModalPopup
+onready var dialog := get_node("/root/main/dialog") as Dialog
+onready var file_picker := get_node("/root/main/file_picker") as FileDialog
+onready var database := get_node("/root/main/database") as Database
+onready var stage := get_node("/root/main/stage") as Stage
+
+
+func _ready():
+ for idx in range(menu_items.size()):
+ menu.add_item(menu_items[idx].label, idx)
+ menu.connect("id_pressed", self, "id_pressed")
+
+
+func id_pressed(id: int):
+ self.call_deferred(menu_items[id].action)
+
+
+func import_option_sets_action():
+ dialog.setup("All option sets from the dropdown menus will be replaced.", "Continue", "No")
+ dialog.connect("accepted", self, "import_option_sets_action_accepted")
+ popup.open_popup("Replace option sets?", dialog)
+
+
+func import_option_sets_action_accepted():
+ file_picker.mode = FileDialog.MODE_OPEN_FILE
+ file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
+ file_picker.filters = ["*.json", "*.csv"]
+ file_picker.current_file = ""
+ file_picker.connect("file_selected", self, "import_option_sets")
+ file_picker.show_modal(true)
+ file_picker.invalidate()
+
+
+func import_option_sets(file_path: String):
+ stage.load_option_sets(file_path)
+ stage.save_option_sets()
+
+
+func export_option_sets_action():
+ file_picker.mode = FileDialog.MODE_SAVE_FILE
+ file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
+ file_picker.filters = ["*.json"]
+ file_picker.current_file = ""
+ file_picker.connect("file_selected", stage, "save_option_sets")
+ file_picker.show_modal(true)
+ file_picker.invalidate()
+
+
+func clear_option_sets_action():
+ dialog.setup("All option sets from the dropdown menus will be deleted.", "Delete all", "No")
+ dialog.connect("accepted", self, "clear_option_sets")
+ popup.open_popup("Clear option sets?", dialog)
+
+
+func clear_option_sets():
+ stage.clear_option_sets()
+ stage.save_option_sets()
+
+
+func import_database_action():
+ dialog.setup("All entries from the database will be replaced.", "Continue", "No")
+ dialog.connect("accepted", self, "import_database_action_accepted")
+ popup.open_popup("Replace database?", dialog)
+
+
+func import_database_action_accepted():
+ file_picker.mode = FileDialog.MODE_OPEN_FILE
+ file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
+ file_picker.filters = ["*.json", "*.csv"]
+ file_picker.current_file = ""
+ file_picker.connect("file_selected", self, "import_database")
+ file_picker.show_modal(true)
+ file_picker.invalidate()
+
+
+func import_database(file_path: String):
+ database.load_database(file_path)
+ database.save_database()
+
+
+func export_database_action():
+ file_picker.mode = FileDialog.MODE_SAVE_FILE
+ file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
+ file_picker.filters = ["*.csv"]
+ file_picker.current_file = ""
+ file_picker.connect("file_selected", database, "save_database")
+ file_picker.show_modal(true)
+ file_picker.invalidate()
+
+
+func clear_database_action():
+ dialog.setup("All entries from the database will be deleted.", "Delete all", "No")
+ dialog.connect("accepted", self, "clear_database")
+ popup.open_popup("Clear database?", dialog)
+
+
+func clear_database():
+ database.clear_database()
+ database.save_database()
+
+
+func export_app_log_action():
+ file_picker.mode = FileDialog.MODE_SAVE_FILE
+ file_picker.current_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS)
+ file_picker.filters = ["*.log"]
+ file_picker.current_file = ""
+ file_picker.connect("file_selected", self, "export_app_log")
+ file_picker.show_modal(true)
+ file_picker.invalidate()
+
+
+func export_app_log(file_path: String):
+ var error : int
+ var file := File.new()
+
+ error = file.open(LOGS_FILE_PATH, File.READ)
+ if error != OK:
+ printerr("Failed to open log file '%s' (error %d)." % [LOGS_FILE_PATH, error])
+ return
+ var file_content = file.get_as_text()
+ file.close()
+
+ error = file.open(file_path, File.WRITE)
+ if error != OK:
+ printerr("Failed to open file '%s' to write log (error %d)." % [file_path, error])
+ return
+ file.store_string(file_content)
+ file.close()
+
+
+func about_action():
+ dialog.setup("Surgery Log\nversion %s" % ProjectSettings.get_setting("global/version"), "", "")
+ popup.open_popup("About", dialog)
+
+
diff --git a/logic/stage.gd b/logic/stage.gd
new file mode 100644
index 0000000..e6d9474
--- /dev/null
+++ b/logic/stage.gd
@@ -0,0 +1,294 @@
+extends TouchVerticalContainer
+class_name Stage
+
+signal save # (database_entry: Dictionary)
+signal discard # ()
+
+const OPTION_SETS_FILE_PATH: String = "user://option_sets.json"
+const OPTION_SETS_FILE_VERSION: String = "SL_OS_V1"
+const OPTION_SETS_NOT_AVAILABLE: String = "--"
+const OPTION_SETS_TREE_STRUCTURE := {
+ "place": null,
+ "anesthesia": null,
+ "first_assistant": null,
+ "type": {
+ "sub_type": {
+ "sub_sub_type": null,
+ "pathology": null,
+ "intervention": null,
+ }
+ }
+}
+
+var staged_entry_hash: int
+var option_sets: Dictionary
+
+onready var title := get_node("controls/title") as Label
+onready var process_id := get_node("controls/process_id") as LineEdit
+onready var surgery_id := get_node("controls/surgery_id") as LineEdit
+onready var date := get_node("controls/date_picker") as DatePicker
+onready var place := get_node("controls/place") as OptionSet
+onready var anesthesia := get_node("controls/anesthesia") as OptionSet
+onready var first_assistant := get_node("controls/first_assistant") as OptionSet
+onready var type := get_node("controls/type") as OptionSet
+onready var sub_type := get_node("controls/sub_type") as OptionSet
+onready var sub_sub_type := get_node("controls/sub_sub_type") as OptionSet
+onready var pathology := get_node("controls/pathology") as OptionSet
+onready var intervention := get_node("controls/intervention") as OptionSet
+onready var is_urgency := get_node("controls/is_urgency") as Button
+onready var notes := get_node("controls/notes") as LineEdit
+onready var save_button := get_node("controls/buttons/save") as Button
+onready var discard_button := get_node("controls/buttons/discard") as Button
+onready var scrollbar := get_v_scrollbar()
+onready var popup := get_node("/root/main/popup") as ModalPopup
+onready var dialog := get_node("/root/main/dialog") as Dialog
+
+
+func _init():
+ exclude_controls = ["date_picker", "save", "discard"]
+ load_option_sets()
+
+
+func _ready():
+ # Fix height of buttons container.
+ (get_node("controls/buttons") as Control).rect_min_size.y = save_button.rect_size.y
+
+ scrollbar.connect("visibility_changed", self, "adjust_layout")
+ save_button.connect("pressed", self, "save_action")
+ discard_button.connect("pressed", self, "discard_action")
+
+ for it in controls.get_children():
+ if it is LineEdit:
+ it.connect("focus_entered", it, "set", ["caret_position", it.max_length])
+ it.connect("focus_exited", it, "deselect")
+
+ # Map option sets buttons.
+ var option_sets_map := {
+ "place": place,
+ "anesthesia": anesthesia,
+ "first_assistant": first_assistant,
+ "type": type,
+ "sub_type": sub_type,
+ "sub_sub_type": sub_sub_type,
+ "pathology": pathology,
+ "intervention": intervention
+ }
+ for key in option_sets_map:
+ var button := option_sets_map[key].get_node("button") as Button
+ button.connect("pressed", self, "show_options", [key])
+
+
+func adjust_layout():
+ var margin_size := scrollbar.rect_size.x
+ self.margin_left = margin_size
+ self.margin_top = margin_size
+ self.margin_bottom = -margin_size
+ self.margin_right = 0.0 if scrollbar.visible else -margin_size
+
+
+func show_options(field: String):
+ var option_set_field := self[field] as OptionSet
+ var options: Array
+
+ match field:
+ "sub_type":
+ options = option_sets.get("type", {}).get(type.text, {}).get(field, {}).keys()
+ "sub_sub_type", "pathology", "intervention":
+ options = option_sets.get("type", {}).get(type.text, {}).get("sub_type", {}).get(sub_type.text, {}).get(field, {}).keys()
+ _:
+ options = option_sets.get(field, {}).keys()
+
+ options.sort()
+ option_set_field.show_options(options)
+
+
+func save_action():
+ self.visible = false
+ var staged_entry := get_stage()
+ gather_option_sets(staged_entry, option_sets)
+ save_option_sets()
+ emit_signal("save", staged_entry)
+
+
+func discard_action():
+ if get_stage().hash() != staged_entry_hash:
+ dialog.setup("Changes made to this entry will be discarded.", "Discard", "No")
+ dialog.connect("accepted", self, "discard_action_confirmed")
+ popup.open_popup("Discard changes?", dialog)
+ else:
+ discard_action_confirmed()
+
+
+func discard_action_confirmed():
+ self.visible = false
+ emit_signal("discard")
+
+
+func set_stage(entry: Dictionary, title: String):
+ self.title.text = title
+ staged_entry_hash = entry.hash()
+ process_id.text = entry.process_id
+ surgery_id.text = entry.surgery_id
+ date.set_date(entry.date_year, entry.date_month, entry.date_day)
+ place.text = entry.place
+ anesthesia.text = entry.anesthesia
+ first_assistant.text = entry.first_assistant
+ type.text = entry.type
+ sub_type.text = entry.sub_type
+ sub_sub_type.text = entry.sub_sub_type
+ pathology.text = entry.pathology
+ intervention.text = entry.intervention
+ is_urgency.pressed = entry.is_urgency
+ notes.text = entry.notes
+ self.scroll_vertical = 0
+
+
+func get_stage() -> Dictionary:
+ var entry := {
+ "process_id": process_id.text,
+ "surgery_id": surgery_id.text,
+ "date_year": date.get_year(),
+ "date_month": date.get_month(),
+ "date_day": date.get_day(),
+ "place": place.text,
+ "anesthesia": anesthesia.text,
+ "first_assistant": first_assistant.text,
+ "type": type.text,
+ "sub_type": sub_type.text,
+ "sub_sub_type": sub_sub_type.text,
+ "pathology": pathology.text,
+ "intervention": intervention.text,
+ "is_urgency": is_urgency.pressed,
+ "notes": notes.text,
+ }
+ return entry
+
+
+static func sanitize_option_sets(entry: Dictionary, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE):
+ # Delete extra keys.
+ var keys_to_delete: Array
+ for key in entry:
+ if blueprint.has(key) == false:
+ keys_to_delete.append(key)
+ for key in keys_to_delete:
+ entry.erase(key)
+
+ for key in blueprint:
+ # Add missing keys.
+ if typeof(entry.get(key)) != TYPE_DICTIONARY:
+ entry[key] = {}
+ # Process sub-keys
+ if blueprint[key] != null:
+ for sub_key in entry[key]:
+ if typeof(entry[key][sub_key]) != TYPE_DICTIONARY:
+ entry[key][sub_key] = {}
+ sanitize_option_sets(entry[key][sub_key], blueprint[key])
+
+
+static func gather_option_sets(entry: Dictionary, target: Dictionary, blueprint: Dictionary = OPTION_SETS_TREE_STRUCTURE):
+ for key in blueprint:
+ if target.get(key) == null:
+ target[key] = {}
+
+ var value := (entry[key] as String).strip_edges()
+ if value == "" || value == OPTION_SETS_NOT_AVAILABLE:
+ continue
+
+ if target[key].has(value) == false:
+ target[key][value] = null if blueprint[key] == null else {}
+
+ if blueprint[key] != null:
+ gather_option_sets(entry, target[key][value], blueprint[key])
+
+
+func save_option_sets(file_path: String = OPTION_SETS_FILE_PATH):
+ match file_path.get_extension():
+ "json":
+ export_json(file_path, option_sets)
+
+# "csv":
+# export_csv(file_path, option_sets)
+
+ _:
+ printerr("Invalid option sets file extension '%s', expected 'json'." % file_path.get_file())
+ return
+
+
+static func export_json(file_path: String, data: Dictionary):
+ var option_sets_file := {
+ "version": OPTION_SETS_FILE_VERSION,
+ "option_sets": data,
+ }
+ var indentation_char := "" if file_path == OPTION_SETS_FILE_PATH else "\t"
+ var file_content := JSON.print(option_sets_file, indentation_char)
+
+ var file := File.new()
+ file.open(file_path, File.WRITE)
+ file.store_string(file_content)
+ file.close()
+
+
+#static func export_csv(file_path: String, data: Dictionary):
+# pass
+
+
+func load_option_sets(file_path: String = OPTION_SETS_FILE_PATH):
+ match file_path.get_extension():
+ "json":
+ clear_option_sets()
+ option_sets = import_json(file_path)
+ if file_path != OPTION_SETS_FILE_PATH:
+ sanitize_option_sets(option_sets)
+
+ "csv":
+ clear_option_sets()
+ option_sets = import_csv(file_path)
+
+ _:
+ printerr("Invalid option sets file extension '%s', expected 'json' or 'csv'." % file_path.get_file())
+ return
+
+
+static func import_json(file_path: String) -> Dictionary:
+ var result := {}
+
+ var file := File.new()
+ var error := file.open(file_path, File.READ_WRITE)
+ if error != OK:
+ printerr("Failed to open option sets file '%s' (error %d)." % [file_path, error])
+ return result
+ var file_content = file.get_as_text()
+ file.close()
+ var parse_result = JSON.parse(file_content)
+
+ if parse_result.error != OK:
+ printerr("Failed to parse option sets file '%s' (error %d)." % [file_path, parse_result.error])
+ return result
+
+ if typeof(parse_result.result) != TYPE_DICTIONARY:
+ printerr("Invalid option sets file type '%s', expected '%s'." % [typeof(parse_result.result), TYPE_DICTIONARY])
+ return result
+
+ if parse_result.result["version"] != OPTION_SETS_FILE_VERSION:
+ printerr("Invalid option sets file version '%s', expected '%s'." % [parse_result.result["version"], OPTION_SETS_FILE_VERSION])
+ return result
+
+ if typeof(parse_result.result["option_sets"]) != TYPE_DICTIONARY:
+ printerr("Invalid option sets content type '%s', expected '%s'." % [typeof(parse_result.result["option_sets"]), TYPE_DICTIONARY])
+ return result
+
+ result = parse_result.result["option_sets"]
+ return result
+
+
+static func import_csv(file_path: String) -> Dictionary:
+ var result := {}
+ for it in Database.import_csv(file_path):
+ gather_option_sets(it, result)
+ return result
+
+
+func clear_option_sets():
+ option_sets = {}
+
+
diff --git a/main.gd b/main.gd
new file mode 100644
index 0000000..ec449df
--- /dev/null
+++ b/main.gd
@@ -0,0 +1,65 @@
+extends Control
+
+var power_throttle_timeout: float
+
+onready var popup := get_node("/root/main/popup") as ModalPopup
+onready var file_picker := get_node("/root/main/file_picker") as FileDialog
+onready var stage := get_node("/root/main/stage") as Stage
+onready var database := get_node("/root/main/database") as Database
+onready var controls_sensible_to_keyboard := [
+ self,
+ file_picker,
+]
+
+
+func _init():
+
+ Physics2DServer.set_active(false)
+ PhysicsServer.set_active(false)
+
+ if OS.get_name() == "Android":
+ var permissions := Array(OS.get_granted_permissions())
+ if permissions.has("android.permission.READ_EXTERNAL_STORAGE") == false \
+ or permissions.has("android.permission.WRITE_EXTERNAL_STORAGE") == false:
+ OS.request_permissions()
+
+
+func _ready():
+ Input.set_use_accumulated_input(false)
+
+
+func _process(delta: float):
+ var keyboard_height: int = OS.get_virtual_keyboard_height()
+ for it in controls_sensible_to_keyboard:
+ it.margin_bottom = -keyboard_height
+
+ if power_throttle_timeout > 0.0:
+ power_throttle_timeout -= delta
+ else:
+ Engine.target_fps = 10
+
+
+func _input(event: InputEvent):
+ Engine.target_fps = 0
+ power_throttle_timeout = 3.5
+
+
+func _unhandled_input(event: InputEvent):
+ Engine.target_fps = 0
+ power_throttle_timeout = 3.5
+
+
+func _notification(what: int):
+ if what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST:
+ if popup.visible:
+ popup.call_deferred("dismiss")
+ elif file_picker.visible:
+ file_picker.hide()
+ elif stage.visible:
+ stage.discard_action()
+ elif database.selected_idx != -1:
+ database.clear_selection()
+ else:
+ get_tree().quit()
+
+
diff --git a/main.tscn b/main.tscn
new file mode 100644
index 0000000..9fce789
--- /dev/null
+++ b/main.tscn
@@ -0,0 +1,277 @@
+[gd_scene load_steps=15 format=2]
+
+[ext_resource path="res://main.gd" type="Script" id=1]
+[ext_resource path="res://ui/date_picker/date_picker.tscn" type="PackedScene" id=2]
+[ext_resource path="res://themes/dark.tres" type="Theme" id=3]
+[ext_resource path="res://logic/menu.gd" type="Script" id=4]
+[ext_resource path="res://logic/database.gd" type="Script" id=5]
+[ext_resource path="res://fonts/font_icons.tres" type="DynamicFont" id=6]
+[ext_resource path="res://ui/option_set/option_set.tscn" type="PackedScene" id=7]
+[ext_resource path="res://ui/option_set/option_set_list.gd" type="Script" id=8]
+[ext_resource path="res://logic/stage.gd" type="Script" id=9]
+[ext_resource path="res://ui/modal_popup/modal_popup.tscn" type="PackedScene" id=10]
+[ext_resource path="res://ui/touch_item_list/touch_item_list.tscn" type="PackedScene" id=11]
+[ext_resource path="res://ui/touch_vertical_container/touch_vertical_container.tscn" type="PackedScene" id=12]
+[ext_resource path="res://ui/dialog/dialog.gd" type="Script" id=14]
+[ext_resource path="res://logic/file_picker.gd" type="Script" id=15]
+
+[node name="main" type="Control"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+theme = ExtResource( 3 )
+script = ExtResource( 1 )
+
+[node name="database" parent="." instance=ExtResource( 11 )]
+script = ExtResource( 5 )
+
+[node name="menu" type="MenuButton" parent="database"]
+anchor_left = 1.0
+anchor_right = 1.0
+margin_left = -112.0
+margin_top = 32.0
+margin_right = -32.0
+margin_bottom = 112.0
+grow_horizontal = 0
+rect_min_size = Vector2( 90, 90 )
+custom_fonts/font = ExtResource( 6 )
+text = ""
+flat = false
+script = ExtResource( 4 )
+
+[node name="actions" type="VBoxContainer" parent="database"]
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_right = -32.0
+margin_bottom = -32.0
+grow_horizontal = 0
+grow_vertical = 0
+custom_constants/separation = 30
+alignment = 2
+
+[node name="delete" type="Button" parent="database/actions"]
+margin_right = 90.0
+margin_bottom = 80.0
+rect_min_size = Vector2( 80, 80 )
+focus_mode = 0
+custom_fonts/font = ExtResource( 6 )
+shortcut_in_tooltip = false
+enabled_focus_mode = 0
+text = ""
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="edit" type="Button" parent="database/actions"]
+margin_top = 110.0
+margin_right = 90.0
+margin_bottom = 190.0
+rect_min_size = Vector2( 80, 80 )
+focus_mode = 0
+custom_fonts/font = ExtResource( 6 )
+shortcut_in_tooltip = false
+enabled_focus_mode = 0
+text = ""
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="add" type="Button" parent="database/actions"]
+margin_top = 220.0
+margin_right = 90.0
+margin_bottom = 310.0
+rect_min_size = Vector2( 90, 90 )
+focus_mode = 0
+custom_fonts/font = ExtResource( 6 )
+shortcut_in_tooltip = false
+enabled_focus_mode = 0
+text = ""
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="stage" parent="." instance=ExtResource( 12 )]
+visible = false
+script = ExtResource( 9 )
+
+[node name="title" type="Label" parent="stage/controls" index="0"]
+margin_right = 1080.0
+margin_bottom = 62.0
+rect_min_size = Vector2( 0, 62 )
+align = 1
+
+[node name="process_id" type="LineEdit" parent="stage/controls" index="1"]
+margin_top = 72.0
+margin_right = 1080.0
+margin_bottom = 134.0
+rect_min_size = Vector2( 0, 62 )
+max_length = 32
+placeholder_text = "Process #"
+caret_blink = true
+caret_blink_speed = 0.5
+
+[node name="surgery_id" type="LineEdit" parent="stage/controls" index="2"]
+margin_top = 144.0
+margin_right = 1080.0
+margin_bottom = 206.0
+rect_min_size = Vector2( 0, 62 )
+max_length = 32
+placeholder_text = "Surgery #"
+caret_blink = true
+caret_blink_speed = 0.5
+
+[node name="date_picker" parent="stage/controls" index="3" instance=ExtResource( 2 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_left = 340.0
+margin_top = 216.0
+margin_right = 740.0
+margin_bottom = 416.0
+rect_min_size = Vector2( 400, 200 )
+size_flags_horizontal = 4
+
+[node name="place" parent="stage/controls" index="4" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 426.0
+margin_right = 1080.0
+margin_bottom = 488.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Place"
+
+[node name="anesthesia" parent="stage/controls" index="5" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 498.0
+margin_right = 1080.0
+margin_bottom = 560.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Anesthesia"
+
+[node name="first_assistant" parent="stage/controls" index="6" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 570.0
+margin_right = 1080.0
+margin_bottom = 632.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "First assistant"
+
+[node name="type" parent="stage/controls" index="7" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 642.0
+margin_right = 1080.0
+margin_bottom = 704.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Type"
+
+[node name="sub_type" parent="stage/controls" index="8" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 714.0
+margin_right = 1080.0
+margin_bottom = 776.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Sub-type"
+
+[node name="sub_sub_type" parent="stage/controls" index="9" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 786.0
+margin_right = 1080.0
+margin_bottom = 848.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Sub-sub-type"
+
+[node name="pathology" parent="stage/controls" index="10" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 858.0
+margin_right = 1080.0
+margin_bottom = 920.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Pathology"
+
+[node name="intervention" parent="stage/controls" index="11" instance=ExtResource( 7 )]
+anchor_right = 0.0
+anchor_bottom = 0.0
+margin_top = 930.0
+margin_right = 1080.0
+margin_bottom = 992.0
+rect_min_size = Vector2( 0, 62 )
+placeholder = "Intervention"
+
+[node name="is_urgency" type="Button" parent="stage/controls" index="12"]
+margin_top = 1002.0
+margin_right = 1080.0
+margin_bottom = 1064.0
+rect_min_size = Vector2( 0, 62 )
+toggle_mode = true
+text = "Is urgency"
+align = 0
+
+[node name="notes" type="LineEdit" parent="stage/controls" index="13"]
+margin_top = 1074.0
+margin_right = 1080.0
+margin_bottom = 1136.0
+rect_min_size = Vector2( 0, 62 )
+max_length = 4096
+placeholder_text = "Notes"
+caret_blink = true
+caret_blink_speed = 0.5
+
+[node name="buttons" type="Control" parent="stage/controls" index="14"]
+margin_top = 1146.0
+margin_right = 1080.0
+margin_bottom = 1146.0
+
+[node name="discard" type="Button" parent="stage/controls/buttons"]
+anchor_right = 0.5
+margin_right = -5.0
+rect_min_size = Vector2( 0, 62 )
+text = "Discard"
+
+[node name="save" type="Button" parent="stage/controls/buttons"]
+anchor_left = 0.5
+anchor_right = 1.0
+margin_left = 5.0
+grow_horizontal = 0
+rect_min_size = Vector2( 0, 62 )
+text = "Save"
+
+[node name="file_picker" type="FileDialog" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+window_title = ""
+mode_overrides_title = false
+access = 2
+filters = PoolStringArray( "*.*" )
+show_hidden_files = true
+current_dir = ""
+current_path = ""
+script = ExtResource( 15 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="popup" parent="." instance=ExtResource( 10 )]
+
+[node name="option_set_list" type="Control" parent="."]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+rect_clip_content = true
+script = ExtResource( 8 )
+
+[node name="dialog" type="Control" parent="."]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+rect_clip_content = true
+script = ExtResource( 14 )
+
+[editable path="stage"]
diff --git a/proguard-project.txt b/proguard-project.txt
deleted file mode 100755
index f2fe155..0000000
--- a/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/project.godot b/project.godot
new file mode 100644
index 0000000..b71ddf9
--- /dev/null
+++ b/project.godot
@@ -0,0 +1,123 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=4
+
+_global_script_classes=[ {
+"base": "TouchItemList",
+"class": "Database",
+"language": "GDScript",
+"path": "res://logic/database.gd"
+}, {
+"base": "Reference",
+"class": "DatabaseEntry",
+"language": "GDScript",
+"path": "res://logic/database_entry.gd"
+}, {
+"base": "Control",
+"class": "DatePicker",
+"language": "GDScript",
+"path": "res://ui/date_picker/date_picker.gd"
+}, {
+"base": "Control",
+"class": "Dialog",
+"language": "GDScript",
+"path": "res://ui/dialog/dialog.gd"
+}, {
+"base": "Control",
+"class": "ModalPopup",
+"language": "GDScript",
+"path": "res://ui/modal_popup/modal_popup.gd"
+}, {
+"base": "Control",
+"class": "OptionSet",
+"language": "GDScript",
+"path": "res://ui/option_set/option_set.gd"
+}, {
+"base": "Control",
+"class": "OptionSetList",
+"language": "GDScript",
+"path": "res://ui/option_set/option_set_list.gd"
+}, {
+"base": "Control",
+"class": "PointerInputSensor",
+"language": "GDScript",
+"path": "res://ui/pointer_input_sensor.gd"
+}, {
+"base": "TouchVerticalContainer",
+"class": "Stage",
+"language": "GDScript",
+"path": "res://logic/stage.gd"
+}, {
+"base": "ItemList",
+"class": "TouchItemList",
+"language": "GDScript",
+"path": "res://ui/touch_item_list/touch_item_list.gd"
+}, {
+"base": "ScrollContainer",
+"class": "TouchVerticalContainer",
+"language": "GDScript",
+"path": "res://ui/touch_vertical_container/touch_vertical_container.gd"
+}, {
+"base": "Control",
+"class": "ValuePicker",
+"language": "GDScript",
+"path": "res://ui/date_picker/value_picker.gd"
+} ]
+_global_script_class_icons={
+"Database": "",
+"DatabaseEntry": "",
+"DatePicker": "",
+"Dialog": "",
+"ModalPopup": "",
+"OptionSet": "",
+"OptionSetList": "",
+"PointerInputSensor": "",
+"Stage": "",
+"TouchItemList": "",
+"TouchVerticalContainer": "",
+"ValuePicker": ""
+}
+
+[application]
+
+config/name="Surgery Log"
+run/main_scene="res://main.tscn"
+boot_splash/image="res://icons/icon.png"
+boot_splash/fullsize=false
+boot_splash/use_filter=false
+boot_splash/bg_color=Color( 0, 0, 0, 0 )
+config/icon="res://icons/icon.png"
+config/quit_on_go_back=false
+
+[display]
+
+window/size/width=1080
+window/size/height=1920
+window/size/test_width=576
+window/size/test_height=1024
+window/energy_saving/keep_screen_on=false
+window/handheld/orientation="portrait"
+
+[global]
+
+version="1.0"
+
+[input_devices]
+
+pointing/emulate_touch_from_mouse=true
+
+[physics]
+
+common/enable_pause_aware_picking=true
+
+[rendering]
+
+vram_compression/import_etc=true
+environment/default_clear_color=Color( 0, 0, 0, 1 )
+environment/default_environment="res://default_env.tres"
diff --git a/project.properties b/project.properties
deleted file mode 100755
index 4ab1256..0000000
--- a/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..89a7ea6
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,82 @@
+Surgery Log
+===========
+
+# Notes
+- Location of `user://`: `~/.local/share/godot/app_userdata/`.
+
+# ToDo
+- [x] Datepicker: On click (without drag) reset velocity to 0; show and focus 'input' to allow introducing value.
+- [x] Selecting last (year?)/month/day and de-selecting it, moves value to next;
+- [x] Solve how entries are shared across db_screen and stage_screen;
+- [x] Remove db_entry;
+- [x] Rename db script/node to database;
+- [x] scrolling down on the database screen jumps to the end of the list immediatelly;
+- [x] edited entry does not show updated once saved;
+- [x] edited entry shows updated when selected then press back button;
+- [x] load/store database CSV file;
+- [x] export database to CSV file;
+- [x] allow to reset database;
+- [x] split touch logic from database (create TouchItemList);
+- [x] split touch logic from stage (create TouchVerticalContainer);
+- [x] load/store filters JSON file;
+- [x] import/export filters to CSV file;
+- [x] add option_sets buttons on stage screen:
+ - should show a pop-up with multiple options filtered according to current filters;
+ - allow options to be scrolled by dragging;
+ - selecting option puts that text on the associated LineEdit;
+- [x] add pop-up asking if changes are to be discarded once the stage screen's discard button is pressed;
+- [x] add pop-up confirming delete-entry action;
+- [x] edit and delete action buttons should be faded-out when no entry is selected;
+- [x] Sort option sets alphabetically;
+- [x] Check and request file access permissions on Android:
+ ```py
+ if OS.get_name() == "Android":
+ var has_permissions := false
+ while not has_permissions:
+ var permissions := Array(OS.get_granted_permissions())
+ if not permissions.has("android.permission.READ_EXTERNAL_STORAGE") \
+ or not permissions.has("android.permission.WRITE_EXTERNAL_STORAGE"):
+ OS.request_permissions()
+ # await get_tree().create_timer(1).timeout
+ yield(get_tree().create_timer(1), "timeout") # - for Godot 3 branch
+ else:
+ has_permissions = true
+ ```
+- [x] Fix the show option sets buttons; they are drawn over the input fields and hide inserted text;
+- [x] The stage control must be set to ignore the mouse, otherwise the touch-sensor conflicts with the built-in scroll;
+- [x] On database, selecting an entry and removing it will leave the action buttons visible while no entry is selected;
+- [x] Tweak 'POINTER_VELOCITY_DECAYING_FACTOR' and 'POINTER_VELOCITY_BOOST_FACTOR' on database and stage screens;
+- [x] Allow to parse option sets from database file;
+- [x] Check if import_option_sets, store_option_sets, store_database require the parameter save_changes; this requires changes on databse, stage and menu scripts;
+- [x] Maybe replace fonts with non-mono version;
+- [x] Fix option sets GUI element to provide word-wrap, otherwise long texts will be hidden;
+- [x] After save and load, entries opened on stage always detect changes (even when no change occurs);
+- [x] Add to popup:
+ - [x] title;
+ - [x] dismiss button;
+- [x] Hide dialogs title bar (appear in the top with 1 or 2 pixels height);
+- [x] Fix automation to automatically disconnect acceptance signal handlers when reject is chosen on file_picker/confirm_action;
+- [x] Use popup to display confirmation messages;
+- [x] Improve option sets: selecting outside optionset entry should select none; if no options are available, show nothing instead of "--";
+- [x] Set stage discard/save buttons side by side;
+- [x] Implement back button logic;
+ - on stage screen should show pop-up asking it changes are to be discarded;
+ - on file-pickers screen should close them;
+ - on about screen should close it;
+ - on auto-fill pop-up, should close it;
+ - on database screen, should deselect selected item, otherwise should quit the app;
+- [x] db entry should be printed as "%9s | %4s | %yyyy-%MM";
+- [x] Cleanup code:
+ - [x] Search by tags @DAM and TODO;
+ - [x] Rename dialog and popup classes;
+ - [x] Reorganize code files;
+- [ ] Create light theme;
+- [ ] Create theme entry in menu and save on settings file;
+- [ ] main/screen_controller is responsible for whos currently on focus, fade animations, input access (enable/disable), and back_key_notification handling.
+ For input use:
+ node.set_process_input(!pause)
+ node.set_process_unhandled_input(!pause)
+ node.set_process_unhandled_key_input(!pause)
+- [ ] Translations:
+ - https://docs.godotengine.org/en/stable/getting_started/workflow/assets/importing_translations.html
+ - https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html#introduction
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 9ef6cfb..0000000
--- a/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 330d0a4..0000000
--- a/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index e6341a6..0000000
--- a/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index 590a40f..0000000
--- a/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml
deleted file mode 100755
index 6cfef6f..0000000
--- a/res/layout/activity_main.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/pager"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity" >
-
- <!--
- This title strip will display the currently visible page title, as well as the page
- titles for adjacent pages.
- -->
-
- <android.support.v4.view.PagerTitleStrip
- android:id="@+id/pager_title_strip"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:background="#33b5e5"
- android:paddingBottom="4dp"
- android:paddingTop="4dp"
- android:textColor="#fff" />
-
-</android.support.v4.view.ViewPager>
diff --git a/res/layout/fragment_file_operations.xml b/res/layout/fragment_file_operations.xml
deleted file mode 100755
index 2703afb..0000000
--- a/res/layout/fragment_file_operations.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/FileOperationsView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity$FileOperationsContext" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <Button
- android:id="@+id/importButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/importConfigs_str" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="25dp" />
-
- <Button
- android:id="@+id/exportDataBase"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/exportDataBase_str" />
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
diff --git a/res/layout/fragment_main_dummy.xml b/res/layout/fragment_main_dummy.xml
deleted file mode 100755
index 57dc8ce..0000000
--- a/res/layout/fragment_main_dummy.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity$DummySectionFragment" >
-
- <TextView
- android:id="@+id/section_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
-</RelativeLayout>
diff --git a/res/layout/fragment_new_entry.xml b/res/layout/fragment_new_entry.xml
deleted file mode 100755
index 350fd2a..0000000
--- a/res/layout/fragment_new_entry.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/NewEntryView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity$NewEntryContext" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <EditText
- android:id="@+id/nProcess"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/nProcess_str"
- android:inputType="number" >
-
- <requestFocus />
- </EditText>
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="5dp" />
-
- <DatePicker
- android:id="@+id/datePicker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:calendarViewShown="false" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="5dp" />
-
- <EditText
- android:id="@+id/nSurgery"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/nSurgery_str"
- android:inputType="number" />
-
- <Spinner
- android:id="@+id/placeSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/place_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/placeOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/place_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/anesthesiaSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/anesthesia_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/anesthesiaOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/anesthesia_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/firstAiderSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/firstaider_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/firstAiderOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/firstaider_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/medTypeSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/type_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/medTypeOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/type_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/medSubTypeSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/subtype_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/medSubTypeOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/subtype_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/medSubSubTypeSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/subsubtype_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/medSubSubTypeOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/subsubtype_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/pathologySpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/pathology_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/pathologyOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/pathology_str"
- android:inputType="textMultiLine" />
-
- <Spinner
- android:id="@+id/interventionSpinner"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:prompt="@string/intervention_str"
- android:spinnerMode="dialog" />
-
- <EditText
- android:id="@+id/interventionOverride"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/intervention_str"
- android:inputType="textMultiLine" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="5dp" />
-
- <CheckBox
- android:id="@+id/urgencyCheckBox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/urgency_str" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="5dp" />
-
- <EditText
- android:id="@+id/notesText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/notes_str"
- android:inputType="textMultiLine" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="25dp" />
-
- <CheckBox
- android:id="@+id/overrideCheckBox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/override_str" />
-
- <Button
- android:id="@+id/fillOverrideButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/filloverride_str" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="25dp" />
-
- <Button
- android:id="@+id/saveButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/save_str" />
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
diff --git a/res/layout/fragment_view_data.xml b/res/layout/fragment_view_data.xml
deleted file mode 100755
index db246f1..0000000
--- a/res/layout/fragment_view_data.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/RelativeLayout1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="bottom"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/refreshBtn"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:text="@string/refresh_str" />
-
- <ListView
- android:id="@+id/dataBaseListView"
- android:layout_width="match_parent"
- android:layout_height="fill_parent"
- android:layout_above="@+id/refreshBtn" >
-
- </ListView>
-
-</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/multiline_spinner_dropdown_item.xml b/res/layout/multiline_spinner_dropdown_item.xml
deleted file mode 100755
index ff05fc2..0000000
--- a/res/layout/multiline_spinner_dropdown_item.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/text1"
- style="?android:attr/spinnerDropDownItemStyle"
- android:singleLine="false"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="45dp"
- android:ellipsize="marquee"
- android:textAlignment="inherit"/>
diff --git a/res/layout/override_new_entry.xml b/res/layout/override_new_entry.xml
deleted file mode 100755
index 320feb6..0000000
--- a/res/layout/override_new_entry.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/OverrideNewEntry"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <EditText
- android:id="@+id/overrideType"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/type_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overrideSubType"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/subtype_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overrideSubSubType"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/subsubtype_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overridePatholoty"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/pathology_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overrideIntervention"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/intervention_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overrideFirstAider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/firstaider_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overridePlace"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/place_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
- <EditText
- android:id="@+id/overrideAnesthesia"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="@string/anesthesia_str"
- android:inputType="text" >
-
- <requestFocus />
- </EditText>
-
-</LinearLayout> \ No newline at end of file
diff --git a/res/menu/main.xml b/res/menu/main.xml
deleted file mode 100755
index d122a4b..0000000
--- a/res/menu/main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item
- android:id="@+id/action_settings"
- android:orderInCategory="100"
- android:showAsAction="never"
- android:title="@string/action_settings"/>
-
-</menu>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
deleted file mode 100755
index c876987..0000000
--- a/res/values-sw600dp/dimens.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<resources>
-
- <!--
- Customize dimensions originally defined in res/values/dimens.xml (such as
- screen margins) for sw600dp devices (e.g. 7" tablets) here.
- -->
-
-</resources>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
deleted file mode 100755
index 0df3067..0000000
--- a/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<resources>
-
- <!--
- Customize dimensions originally defined in res/values/dimens.xml (such as
- screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
- -->
- <dimen name="activity_horizontal_margin">128dp</dimen>
-
-</resources>
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
deleted file mode 100755
index e3ef53d..0000000
--- a/res/values-v11/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<resources>
-
- <!--
- Base application theme for API 11+. This theme completely replaces
- AppBaseTheme from res/values/styles.xml on API 11+ devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
- <!-- API 11 theme customizations can go here. -->
- </style>
-
-</resources>
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
deleted file mode 100755
index 94dd245..0000000
--- a/res/values-v14/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<resources>
-
- <!--
- Base application theme for API 14+. This theme completely replaces
- AppBaseTheme from BOTH res/values/styles.xml and
- res/values-v11/styles.xml on API 14+ devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
- <!-- API 14 theme customizations can go here. -->
- </style>
-
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
deleted file mode 100755
index 2e0e2ae..0000000
--- a/res/values/dimens.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<resources>
-
- <!-- Default screen margins, per the Android Design guidelines. -->
- <dimen name="activity_horizontal_margin">16dp</dimen>
- <dimen name="activity_vertical_margin">16dp</dimen>
-
-</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
deleted file mode 100755
index f17b4ad..0000000
--- a/res/values/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="app_name">MedLog</string>
- <string name="action_settings">Settings</string>
- <string name="title_section1">Nova Entrada</string>
- <string name="title_section2">Histórico</string>
- <string name="title_section3">Configurações</string>
- <string name="nProcess_str">Nº Processo</string>
- <string name="nSurgery_str">Nº Cirurgia</string>
- <string name="urgency_str">Urgência</string>
- <string name="importConfigs_str">Importar Configurações</string>
- <string name="exportDataBase_str">Exportar Dados</string>
- <string name="save_str">Guardar</string>
- <string name="ok_str">Ok</string>
- <string name="delete_str">APAGAR</string>
- <string name="refresh_str">Actualizar</string>
- <string name="exportMessage_str">Dados exportados para: </string>
- <string name="exportMessageError_str">Falha ao exportar dados!</string>
- <string name="errorMissingProcessNumber_str">Falta inserir o número do processo!</string>
- <string name="successSavingEntry_str">Nova entrada guardada com sucesso!</string>
-
- <string name="yes_str">sim</string>
- <string name="no_str">não</string>
- <string name="override_str">Costumizar</string>
- <string name="filloverride_str">Usar valores anteriores</string>
-
- <string name="type_str">Tipo</string>
- <string name="subtype_str">Subtipo</string>
- <string name="subsubtype_str">Sub-Subtipo</string>
- <string name="pathology_str">Patologia</string>
- <string name="intervention_str">Intervenção</string>
- <string name="firstaider_str">1º Ajudante</string>
- <string name="anesthesia_str">Anestesia</string>
- <string name="place_str">Local</string>
- <string name="notes_str">Notas</string>
-
-</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
deleted file mode 100755
index 4ea9326..0000000
--- a/res/values/styles.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<resources>
-
- <!--
- Base application theme, dependent on API level. This theme is replaced
- by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Light">
- <!--
- Theme customizations available in newer API levels can go in
- res/values-vXX/styles.xml, while customizations related to
- backward-compatibility can go here.
- -->
- </style>
-
- <!-- Application theme. -->
- <style name="AppTheme" parent="AppBaseTheme">
- <!-- All customizations that are NOT specific to a particular API-level can go here. -->
- </style>
-
-</resources>
diff --git a/src/com/example/surgerylog/FileOperationsFragment.java b/src/com/example/surgerylog/FileOperationsFragment.java
deleted file mode 100755
index 512f02b..0000000
--- a/src/com/example/surgerylog/FileOperationsFragment.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package com.example.surgerylog;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Calendar;
-import java.util.List;
-import net.redroid.medlog.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import com.csvreader.CsvReader;
-import com.csvreader.CsvWriter;
-
-public class FileOperationsFragment extends Fragment {
- /**
- * The fragment argument representing the section number for this fragment.
- */
- public static final String ARG_SECTION_NUMBER = "section_number";
-
- MainActivity _activity;
-
- public FileOperationsFragment() {
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- // This makes sure that the container activity has implemented
- // the callback interface. If not, it throws an exception
- try {
- _activity = (MainActivity) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnHeadlineSelectedListener");
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // get root view
- View rootView = inflater.inflate(R.layout.fragment_file_operations,
- container, false);
-
- return rootView;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // set import MedLog config button
- Button importFile = (Button) getView().findViewById(R.id.importButton);
- importFile.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- try {
- // create csv values
- CsvReader reader = new CsvReader(
- _activity.TemplateDescriptionFile);
-
- // read headers
- if (reader.readHeaders() == false) {
- // could not read headers
- //Log.e("Error", "Headers could not be read.");
- }
-
- // copy back headers
- String headers[] = reader.getHeaders();
-
- // check number of headers
- if (headers.length != _activity.ProcessDescriptionFileNumColumns) {
- // number of headers is incorrect
- //Log.e("Error", "CSV has incorrect number of headers.");
- } else {
- // everything seems nice.... lets rebuild the tree
- _activity.Root = new MedLogRoot();
- }
-
- // read records
- while (reader.readRecord() == true) {
- // get record info into
- MedLogInfo surgeryInfo = new MedLogInfo(reader
- .get(headers[0]), // med type
- reader.get(headers[1]), // med subtype
- reader.get(headers[2]), // med sub-subtype
- reader.get(headers[3]), // pathology
- reader.get(headers[4]) // intervention
- );
-
- surgeryInfo.FirstAider = reader.get(headers[5]); // first
- // aider
- surgeryInfo.Anesthesia = reader.get(headers[6]); // anesthesia
- surgeryInfo.Place = reader.get(headers[7]); // place
-
- // add info to grow tree
- _activity.Root.GrowTreeWithInfo(surgeryInfo);
- }
-
- // close csv reading file
- reader.close();
- } catch (FileNotFoundException e) {
- //Log.e("Error", "Config file not found.");
- } catch (IOException e) {
- //Log.e("Error", "Config file IO error.");
- }
-
- _activity.SaveMedLogConfig();
- }
- });
-
- // set export data base button
- Button exportDbBtn = (Button) getView().findViewById(
- R.id.exportDataBase);
- exportDbBtn.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
-
- //Log.e("Trace Export", "Exporting data");
-
- CsvWriter writer = null;
- boolean success = false;
- try {
-
- //Log.e("Trace Export", "Trying to open file");
-
- writer = new CsvWriter(_activity.ExportFile);
-
- //Log.e("Trace Export", "Writing headers");
-
- // Write descriptive headers.
- String[] headers = { "process number", "surgery number",
- "date", "type", "subtype", "subsubtype",
- "pathology", "intervention", "first aider",
- "anesthesia", "place", "urgency", "notes" };
- writer.writeRecord(headers);
-
- //Log.e("Trace Export", "Writing records...");
-
- // write all the records to CSV
- String[] record = new String[headers.length];
- for (MedLogInfo log : _activity.DataBase) {
- record[0] = log.ProcessNumber.toString();
- record[1] = log.SurgeryNumber.toString();
- record[2] = log.GetFormatedDate();
- record[3] = log.Type;
- record[4] = log.SubType;
- record[5] = log.SubSubType;
- record[6] = log.Pathology;
- record[7] = log.Intervention;
- record[8] = log.FirstAider;
- record[9] = log.Anesthesia;
- record[10] = log.Place;
- record[11] = _activity.getString(log.Urgency == true ? R.string.yes_str : R.string.no_str);
- record[12] = log.Notes;
- writer.writeRecord(record);
- }
-
- String message = _activity.getString(R.string.exportMessage_str)
- + "'"
- + _activity.ExportFile + "'";
- _activity.ShowMessage(message);
-
- //Log.e("Trace Export", "Done");
-
- success = true;
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- writer.close();
- if (success == false) {
- _activity.ShowMessage(R.string.exportMessageError_str);
- }
- }
- }
- });
-
- super.onActivityCreated(savedInstanceState);
- }
-}
diff --git a/src/com/example/surgerylog/ISpinnerRefresh.java b/src/com/example/surgerylog/ISpinnerRefresh.java
deleted file mode 100755
index a8baf36..0000000
--- a/src/com/example/surgerylog/ISpinnerRefresh.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.example.surgerylog;
-
-import java.util.List;
-
-public interface ISpinnerRefresh {
-
- void SetSpinnerData(int spinnerId, List<String> spinnerData);
-
-}
diff --git a/src/com/example/surgerylog/MainActivity.java b/src/com/example/surgerylog/MainActivity.java
deleted file mode 100755
index 47910cc..0000000
--- a/src/com/example/surgerylog/MainActivity.java
+++ /dev/null
@@ -1,330 +0,0 @@
-package com.example.surgerylog;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.StreamCorruptedException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Locale;
-import java.util.Map;
-import net.redroid.medlog.R;
-
-import com.csvreader.CsvReader;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Environment;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.app.NavUtils;
-import android.support.v4.view.ViewPager;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class MainActivity extends FragmentActivity
-{
- /**
- * The {@link android.support.v4.view.PagerAdapter} that will provide
- * fragments for each of the sections. We use a
- * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
- * will keep every loaded fragment in memory. If this becomes too memory
- * intensive, it may be best to switch to a
- * {@link android.support.v4.app.FragmentStatePagerAdapter}.
- */
- SectionsPagerAdapter mSectionsPagerAdapter;
-
- /**
- * The {@link ViewPager} that will host the section contents.
- */
- ViewPager mViewPager;
-
- // MedTree root
- public MedLogRoot Root;
- public List<MedLogInfo> DataBase;
- public MedLogInfo CurrentInfo = new MedLogInfo();
-
- // MedTree description file
- public int ProcessDescriptionFileNumColumns = 8;
- public String AppFolder = Environment.getExternalStorageDirectory() + "//MedLog";
- public String TemplateDescriptionFile = Environment.getExternalStorageDirectory() + "//MedLog//template.csv";
- public String ExportFile = Environment.getExternalStorageDirectory() + "//MedLog//export.csv";
- public String MedLogConfigFile = Environment.getExternalStorageDirectory() + "//MedLog//config.bin";
- public String MedLogDataBaseFile = Environment.getExternalStorageDirectory() + "//MedLog//database.bin";
- private int MessageTime = 1;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- //Log.e("Trace", "MainActiviyy START");
-
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- // Create root
- Root = new MedLogRoot();
-
- // Create the adapter that will return a fragment for each of the three
- // primary sections of the app.
- mSectionsPagerAdapter = new SectionsPagerAdapter(
- getSupportFragmentManager());
-
- // Set up the ViewPager with the sections adapter.
- mViewPager = (ViewPager) findViewById(R.id.pager);
- mViewPager.setAdapter(mSectionsPagerAdapter);
-
- CheckAppFolder();
- LoadMedLogConfig();
- LoadMedLogDB();
-
- //Log.e("Trace", "MainActiviyy OUT");
- }
-
-// @Override
-// public boolean onCreateOptionsMenu(Menu menu) {
-// // Inflate the menu; this adds items to the action bar if it is present.
-// getMenuInflater().inflate(R.menu.main, menu);
-// return true;
-// }
-
- private void CheckAppFolder()
- {
- File appFolder = new File(AppFolder);
- if(appFolder.exists() == false)
- {
- appFolder.mkdir();
- }
- }
-
- public void LoadMedLogConfig()
- {
- try {
- //Log.e("Trace Cfg", "input file");
- FileInputStream fis = new FileInputStream(MedLogConfigFile);
- //FileInputStream fis = getApplicationContext().openFileInput(MedLogConfigFile);
- //FileInputStream fis = getApplicationContext().openFileInput("MedLogConfig.bin");
- //Log.e("Trace Cfg", "input stream");
- ObjectInputStream is = new ObjectInputStream(fis);
- //Log.e("Trace Cfg", "read object");
- Root = (MedLogRoot) is.readObject();
- //Log.e("Trace Cfg", "close file");
- is.close();
- //Log.e("Trace Cfg", "done loading");
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- //e.printStackTrace();
- //Log.e("TrTrace Cfgace", "File not found during load.");
- } catch (StreamCorruptedException e) {
- // TODO Auto-generated catch block
- //e.printStackTrace();
- //Log.e("Trace Cfg", "Stream corrupted during load.");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- //e.printStackTrace();
- //Log.e("Trace Cfg", "IO Exception during load.");
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- //e.printStackTrace();
- //Log.e("Trace Cfg", "Class not found during load.");
- }
- }
-
- public void SaveMedLogConfig()
- {
- try {
- //Log.e("Trace Cfg", "get context");
- Context c = getApplicationContext();
- //Log.e("Trace Cfg", "open output file");
- //FileOutputStream fos = getApplicationContext().openFileOutput(MedLogConfigFile, Context.MODE_PRIVATE);
- FileOutputStream fos = new FileOutputStream(MedLogConfigFile);
- //Log.e("Trace Cfg", "new output stream");
- ObjectOutputStream os = new ObjectOutputStream(fos);
- //Log.e("Trace Cfg", "write");
- os.writeObject(Root);
- //Log.e("Trace Cfg", "close");
- os.close();
- //Log.e("Trace Cfg", "done");
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- //Log.e("Trace Cfg", "File not found during save.");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- //Log.e("Trace Cfg", "IO Exception during save.");
- }
- }
-
- public void LoadMedLogDB()
- {
- try {
- //Log.e("Trace DB", "input file");
- FileInputStream fis = new FileInputStream(MedLogDataBaseFile);
- //Log.e("Trace DB", "input stream");
- ObjectInputStream is = new ObjectInputStream(fis);
- //Log.e("Trace DB", "read object");
- DataBase = (List<MedLogInfo>) is.readObject();
- //Log.e("Trace DB", "close file");
- is.close();
- //Log.e("Trace DB", "done loading");
- } catch (FileNotFoundException e) {
- //Log.e("Trace DB", "File not found during load.");
- } catch (StreamCorruptedException e) {
- //Log.e("Trace DB", "Stream corrupted during load.");
- } catch (IOException e) {
- //Log.e("Trace DB", "IO Exception during data base load.");
- } catch (ClassNotFoundException e) {
- //Log.e("Trace DB", "Class 'List<MedLogInfo>' not found during load.");
- }
- finally
- {
- if(DataBase == null)
- {
- // create empty data base
- DataBase = new ArrayList<MedLogInfo>();
- }
- }
- }
-
- public void SaveMedLogDB()
- {
- try {
- //Log.e("Trace DB", "get context");
- Context c = getApplicationContext();
- //Log.e("Trace DB", "open output file");
- FileOutputStream fos = new FileOutputStream(MedLogDataBaseFile);
- //Log.e("Trace DB", "new output stream");
- ObjectOutputStream os = new ObjectOutputStream(fos);
- //Log.e("Trace DB", "write");
- os.writeObject(DataBase);
- //Log.e("Trace DB", "close");
- os.close();
- //Log.e("Trace DB", "done");
- } catch (FileNotFoundException e) {
- //Log.e("Trace DB", "File not found during save.");
- } catch (IOException e) {
- //Log.e("Trace DB", "IO Exception during save.");
- }
- }
-
- public void AddNewEntry()
- {
- DataBase.add(CurrentInfo);
- SaveMedLogDB();
- Root.GrowTreeWithInfo(CurrentInfo);
- SaveMedLogConfig();
- CurrentInfo = new MedLogInfo();
- }
-
- public void ShowMessage(int messageId)
- {
- for (int i=0; i < MessageTime; i++)
- {
- Toast.makeText(this, messageId, Toast.LENGTH_LONG).show();
- }
- }
-
- public void ShowMessage(String message)
- {
- for (int i=0; i < MessageTime; i++)
- {
- Toast.makeText(this, message, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
- * one of the sections/tabs/pages.
- */
- public class SectionsPagerAdapter extends FragmentStatePagerAdapter //FragmentPagerAdapter
- {
-
- public SectionsPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public Fragment getItem(int position)
- {
- //Log.e("Trace", "new frag IN");
-
- // getItem is called to instantiate the fragment for the given page.
- // Return a DummySectionFragment (defined as a static inner class
- // below) with the page number as its lone argument.
- Fragment fragment;
- if(position == 0)
- {
- fragment = new NewEntryFragment();
- }
- else if(position == 1)
- {
- fragment = new ViewDataFragment();
- }
- else
- {
- fragment = new FileOperationsFragment();
- }
-
- Bundle args = new Bundle();
- args.putInt(NewEntryFragment.ARG_SECTION_NUMBER, position + 1);
- fragment.setArguments(args);
-
- // retain instances
- //Log.e("Trace", "new frag");
-
- return fragment;
- }
-
- @Override
- public int getCount() {
- // Show 3 total pages.
- return 3;
- }
-
- @Override
- public CharSequence getPageTitle(int position)
- {
- Locale l = Locale.getDefault();
- switch (position) {
- case 0:
- return getString(R.string.title_section1).toUpperCase(l);
- case 1:
- return getString(R.string.title_section2).toUpperCase(l);
- case 2:
- return getString(R.string.title_section3).toUpperCase(l);
- }
- return null;
- }
- }
-
-
-}
diff --git a/src/com/example/surgerylog/MedLogInfo.java b/src/com/example/surgerylog/MedLogInfo.java
deleted file mode 100755
index c59bd29..0000000
--- a/src/com/example/surgerylog/MedLogInfo.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.example.surgerylog;
-
-import java.io.Serializable;
-import java.util.Calendar;
-
-public class MedLogInfo implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 1109021350324730228L;
-
- private static final int MONTH_STARTS_IN_0 = 1;
-
- public Long ProcessNumber;
- public Long SurgeryNumber;
-
- public String GetFormatedDate()
- {
- return Date.get(Calendar.DAY_OF_MONTH) + "/"
- + (Date.get(Calendar.MONTH) + MONTH_STARTS_IN_0) + "/"
- + Date.get(Calendar.YEAR);
- }
- public Calendar Date = Calendar.getInstance();
-
- public String Type;
- public String SubType;
- public String SubSubType;
- public String Pathology;
- public String Intervention;
-
- public String FirstAider;
- public String Anesthesia;
- public String Place;
- public Boolean Urgency;
- public String Notes;
-
- public MedLogInfo()
- {
- super();
- }
-
- public MedLogInfo(String infoType, String infoSubType, String infoSubSubType, String infoPathology, String infoIntervention) {
- super();
- this.Type = infoType.trim();
- this.SubType = infoSubType.trim();
- this.SubSubType = infoSubSubType.trim();
- this.Pathology = infoPathology.trim();
- this.Intervention = infoIntervention.trim();
- }
-}
diff --git a/src/com/example/surgerylog/MedLogRoot.java b/src/com/example/surgerylog/MedLogRoot.java
deleted file mode 100755
index 073701b..0000000
--- a/src/com/example/surgerylog/MedLogRoot.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package com.example.surgerylog;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-public class MedLogRoot implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 7337501254153966693L;
- private Dictionary<String, MedLogType> _typeDictionary = new Hashtable<String, MedLogType>();
- private List<String> _typeList = new ArrayList<String>();
-
- private List<String> _firsAiderList = new ArrayList<String>();
- private List<String> _anesthesiaList = new ArrayList<String>();
- private List<String> _placeList = new ArrayList<String>();
-
- public MedLogRoot() {
- }
-
- public void GrowTreeWithInfo(MedLogInfo info) {
- // try to add new first aider
- if (HasFirstAider(info.FirstAider) == false) {
- AddFirstAider(info.FirstAider);
- }
-
- // try to add new anesthesia
- if (HasAnesthesia(info.Anesthesia) == false) {
- AddAnesthesia(info.Anesthesia);
- }
-
- // try to add new place
- if (HasPlace(info.Place) == false) {
- AddPlace(info.Place);
- }
-
- // try to add new Type
- if (HasType(info.Type) == false) {
- AddType(info.Type);
- }
-
- // Only add branches if has root...
- if (HasType(info.Type) == true) {
-
- // get Type
- MedLogType type = _typeDictionary.get(info.Type);
-
- // add Type related information
- if (type.HasSubType(info.SubType) == false) {
- type.AddSubType(info.SubType);
- }
-
- // Only add branches if has root...
- if (type.HasSubType(info.SubType) == true) {
-
- // get SubType
- MedLogSubType subType = type.GetMedLogSubType(info.SubType);
-
- // add SubTyppe related information
- if (subType.HasSubSubType(info.SubSubType) == false) {
- subType.AddSubSubType(info.SubSubType);
- }
- if (subType.HasPathology(info.Pathology) == false) {
- subType.AddPathology(info.Pathology);
- }
- if (subType.HasIntervention(info.Intervention) == false) {
- subType.AddIntervention(info.Intervention);
- }
- }
- }
- }
-
- private Boolean IsValid(String verify) {
- return verify.trim().isEmpty() == false;
- }
-
- public Boolean HasFirstAider(String firstAider) {
- return _firsAiderList.contains(firstAider.trim());
- }
-
- public void AddFirstAider(String firstAider) {
- if (IsValid(firstAider))
- _firsAiderList.add(firstAider.trim());
- }
-
- public Boolean HasAnesthesia(String anesthesia) {
- return _anesthesiaList.contains(anesthesia.trim());
- }
-
- public void AddAnesthesia(String anesthesia) {
- if (IsValid(anesthesia))
- _anesthesiaList.add(anesthesia.trim());
- }
-
- public Boolean HasPlace(String place) {
- return _placeList.contains(place.trim());
- }
-
- public void AddPlace(String place) {
- if (IsValid(place))
- _placeList.add(place.trim());
- }
-
- public Boolean HasType(String type) {
- return _typeList.contains(type.trim());
- }
-
- public void AddType(String type) {
- if (IsValid(type)) {
- String typeTrim = type.trim();
-
- _typeList.add(typeTrim);
-
- MedLogType surgeryType = new MedLogType();
- _typeDictionary.put(typeTrim, surgeryType);
- }
- }
-
- public MedLogType GetMedLogType(String type) {
- return _typeDictionary.get(type.trim());
- }
-
- public List<String> GetTypeList() {
- return _typeList;
- }
-
- public List<String> GetFirstAiderList() {
- return _firsAiderList;
- }
-
- public List<String> GetAnesthesiaList() {
- return _anesthesiaList;
- }
-
- public List<String> GetPlaceList() {
- return _placeList;
- }
-}
diff --git a/src/com/example/surgerylog/MedLogSubType.java b/src/com/example/surgerylog/MedLogSubType.java
deleted file mode 100755
index 7bac538..0000000
--- a/src/com/example/surgerylog/MedLogSubType.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.example.surgerylog;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-
-public class MedLogSubType implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 964773707334886468L;
- public List<String> _subSubTypeList = new ArrayList<String>();
- public List<String> _pathologyList = new ArrayList<String>();
- public List<String> _interventionList = new ArrayList<String>();
-
- public MedLogSubType()
- {
- }
-
- private Boolean IsValid(String verify) {
- return verify.trim().isEmpty() == false;
- }
-
- public Boolean HasSubSubType(String subSubType)
- {
- return _subSubTypeList.contains(subSubType.trim());
- }
-
- public Boolean HasPathology(String pathology)
- {
- return _pathologyList.contains(pathology.trim());
- }
-
- public Boolean HasIntervention(String intervention)
- {
- return _interventionList.contains(intervention.trim());
- }
-
- public void AddSubSubType(String subSubType)
- {
- if(IsValid(subSubType))
- _subSubTypeList.add(subSubType.trim());
- }
-
- public void AddPathology(String pathology)
- {
- if(IsValid(pathology))
- _pathologyList.add(pathology.trim());
- }
-
- public void AddIntervention(String intervention)
- {
- if(IsValid(intervention))
- _interventionList.add(intervention.trim());
- }
-
- public List<String> GetSubSubTypeList()
- {
- return _subSubTypeList;
- }
-
- public List<String> GetPathologyList()
- {
- return _pathologyList;
- }
-
- public List<String> GetInterventionList()
- {
- return _interventionList;
- }
-
- public int GetNumberOfSubSubTypes()
- {
- return _subSubTypeList.size();
- }
-
- public int GetNumberOfPathologies()
- {
- return _pathologyList.size();
- }
-
- public int GetNumberOfInterventions()
- {
- return _interventionList.size();
- }
-}
diff --git a/src/com/example/surgerylog/MedLogType.java b/src/com/example/surgerylog/MedLogType.java
deleted file mode 100755
index d4bc485..0000000
--- a/src/com/example/surgerylog/MedLogType.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.example.surgerylog;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-
-public class MedLogType implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 7877803213238007254L;
- private Dictionary<String, MedLogSubType> _subTypeDictionary = new Hashtable<String, MedLogSubType>();
- private List<String> _subTypeList = new ArrayList<String>();
-
- public MedLogType()
- {
- }
-
- private Boolean IsValid(String verify) {
- return verify.trim().isEmpty() == false;
- }
-
- public Boolean HasSubType(String subType)
- {
- return _subTypeList.contains(subType.trim());
- }
-
- public MedLogSubType GetMedLogSubType(String subType)
- {
- return _subTypeDictionary.get(subType.trim());
- }
-
- public void AddSubType(String subType)
- {
- if (IsValid(subType)) {
- String subTypeTrim = subType.trim();
-
- _subTypeList.add(subTypeTrim);
-
- MedLogSubType surgerySubType = new MedLogSubType();
- _subTypeDictionary.put(subTypeTrim, surgerySubType);
- }
- }
-
- public List<String> GetSubTypeList()
- {
- return _subTypeList;
- }
-}
diff --git a/src/com/example/surgerylog/NewEntryFragment.java b/src/com/example/surgerylog/NewEntryFragment.java
deleted file mode 100755
index 522d76b..0000000
--- a/src/com/example/surgerylog/NewEntryFragment.java
+++ /dev/null
@@ -1,437 +0,0 @@
-package com.example.surgerylog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.SortedSet;
-import net.redroid.medlog.R;
-
-import android.R.color;
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.sax.TextElementListener;
-import android.support.v4.app.Fragment;
-import android.text.method.DateTimeKeyListener;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewDebug.FlagToString;
-import android.webkit.WebView.FindListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.DatePicker;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.AdapterView.OnItemSelectedListener;
-
-public class NewEntryFragment extends Fragment {
- /**
- * The fragment argument representing the section number for this fragment.
- */
- public static final String ARG_SECTION_NUMBER = "section_number";
-
- private MainActivity _activity;
-
- public NewEntryFragment() {
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- // This makes sure that the container activity has implemented
- // the callback interface. If not, it throws an exception
- try {
- _activity = (MainActivity) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnHeadlineSelectedListener");
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // get root view
- View rootView = inflater.inflate(R.layout.fragment_new_entry,
- container, false);
-
- return rootView;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- // get view
- View view = getView();
-
- // set TYPE spinner listener
- Spinner typeSpinner = (Spinner) view.findViewById(R.id.medTypeSpinner);
- typeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
- // An item was selected. You can retrieve the selected item using
- // parent.getItemAtPosition(pos)
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int pos, long id) {
- // get TYPE
- MedLogType medType = _activity.Root.GetMedLogType(parent
- .getSelectedItem().toString());
-
- // set TYPE child spinner
- SetSpinnerData(R.id.medSubTypeSpinner, medType.GetSubTypeList());
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> arg0) {
- // TODO Auto-generated method stub
- }
- });
-
- // set SUBTYPE spinner listener
- Spinner subTypeSpinner = (Spinner) view
- .findViewById(R.id.medSubTypeSpinner);
- subTypeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
- // An item was selected. You can retrieve the selected item using
- // parent.getItemAtPosition(pos)
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int pos, long id) {
- // get selected TYPE
- Spinner typeSpinner = (Spinner) getView().findViewById(
- R.id.medTypeSpinner);
- String selectedType = typeSpinner.getSelectedItem().toString();
- MedLogType medType = _activity.Root.GetMedLogType(selectedType);
-
- // get selected SUBTYPE item
- MedLogSubType medSubtype = medType.GetMedLogSubType(parent
- .getSelectedItem().toString());
-
- // set SUBTYPE children spinners
- SetSpinnerData(R.id.medSubSubTypeSpinner,
- medSubtype.GetSubSubTypeList());
- SetSpinnerData(R.id.pathologySpinner,
- medSubtype.GetPathologyList());
- SetSpinnerData(R.id.interventionSpinner,
- medSubtype.GetInterventionList());
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> arg0) {
- // TODO Auto-generated method stub
- }
- });
-
- // set save entry button
- Button saveBtn = (Button) getView().findViewById(R.id.saveButton);
- saveBtn.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
-
- // if process number is missing... do not accept
- if (((EditText) getView().findViewById(R.id.nProcess)).getText()
- .toString().trim().isEmpty() == true) {
- _activity.ShowMessage(R.string.errorMissingProcessNumber_str);
- return;
- }
-
- // gather med log info
- GatherMedLogInfo();
-
- // apply overriden fields
- if (OverrideIsActivate()) {
- ApplyOverride();
- }
-
- // Log.e("Trace NewEntry", "All info collected");
-
- _activity.AddNewEntry();
-
- _activity.ShowMessage(R.string.successSavingEntry_str);
-
- // reset all entries to the initial state
- ResetAllEntries();
-
- // Log.e("Trace NewEntry", "Entry saved");
- }
- });
-
- // set override check box
- CheckBox overrideCheck = (CheckBox) getView().findViewById(
- R.id.overrideCheckBox);
- overrideCheck.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View arg0) {
- if (OverrideIsActivate()) {
- ShowOverrideFields();
- } else {
- HideOverrideFields();
- }
- }
- });
-
- // set fill override text button
- Button fillOverrideBtn = (Button) getView().findViewById(
- R.id.fillOverrideButton);
- fillOverrideBtn.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View arg0) {
- FillOverrideFromSpinners();
- }
- });
-
- // hide override fields
- HideOverrideFields();
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- // update type spinner based on current activity root
- SetSpinnerData(R.id.medTypeSpinner, _activity.Root.GetTypeList());
-
- // update first aider spinner
- SetSpinnerData(R.id.firstAiderSpinner,
- _activity.Root.GetFirstAiderList());
-
- // update anesthesia spinner
- SetSpinnerData(R.id.anesthesiaSpinner,
- _activity.Root.GetAnesthesiaList());
-
- // update place spinner
- SetSpinnerData(R.id.placeSpinner, _activity.Root.GetPlaceList());
- }
-
- private void SetSpinnerData(int spinnerId, List<String> spinnerValues) {
- // create array adapter
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(getView()
- .getContext(),
- // android.R.layout.simple_spinner_dropdown_item,
- R.layout.multiline_spinner_dropdown_item, spinnerValues);
-
- // sort spinner values
- adapter.sort(new Comparator<String>() {
- public int compare(String object1, String object2) {
- return object1.compareTo(object2);
- }
- });
-
- // get spinner
- Spinner spinner = (Spinner) getView().findViewById(spinnerId);
-
- // set adapter
- spinner.setAdapter(adapter);
- }
-
- private Spinner GetSpinner(int spinnerId) {
- return (Spinner) getView().findViewById(spinnerId);
- }
-
- private EditText GetEditText(int editTextId) {
- return (EditText) getView().findViewById(editTextId);
- }
-
- private CheckBox GetCheckBox(int checkBoxId){
- return (CheckBox) getView().findViewById(checkBoxId);
- }
-
- private String GetTextFromEditText(int editTextId) {
- return ((EditText) getView().findViewById(editTextId)).getText().toString();
- }
-
- private Boolean OverrideIsActivate() {
- return ((CheckBox) getView().findViewById(R.id.overrideCheckBox))
- .isChecked();
- }
-
- private String GetTextFromSpinner(int spinnerId) {
- Object item = ((Spinner) getView().findViewById(spinnerId)).getSelectedItem();
- if (item != null)
- return item.toString();
- else
- return "";
- }
-
- private void GatherMedLogInfo() {
- // Log.e("Trace NewEntry", "Save requested...");
-
- // collect med log info
- _activity.CurrentInfo.ProcessNumber = Long
- .parseLong(((EditText) getView().findViewById(R.id.nProcess))
- .getText().toString());
-
- // check if surgery number is empty
- if (((EditText) getView().findViewById(R.id.nSurgery)).getText()
- .toString().trim().isEmpty() == true) {
- _activity.CurrentInfo.SurgeryNumber = (long) 0;
- } else {
- _activity.CurrentInfo.SurgeryNumber = Long
- .parseLong(((EditText) getView()
- .findViewById(R.id.nSurgery)).getText().toString());
- }
-
- // Log.e("Trace NewEntry", "Collected numbers");
-
- DatePicker date = ((DatePicker) getView().findViewById(R.id.datePicker));
- _activity.CurrentInfo.Date.set(date.getYear(), date.getMonth(),
- date.getDayOfMonth());
-
- // Log.e("Trace NewEntry", "Collected date");
-
- // get spinners (avoid empty ones)
- _activity.CurrentInfo.Type = GetTextFromSpinner(R.id.medTypeSpinner);
- _activity.CurrentInfo.SubType = GetTextFromSpinner(R.id.medSubTypeSpinner);
-
- _activity.CurrentInfo.SubSubType = GetTextFromSpinner(R.id.medSubSubTypeSpinner);
- _activity.CurrentInfo.Pathology = GetTextFromSpinner(R.id.pathologySpinner);
- _activity.CurrentInfo.Intervention = GetTextFromSpinner(R.id.interventionSpinner);
- _activity.CurrentInfo.FirstAider = GetTextFromSpinner(R.id.firstAiderSpinner);
- _activity.CurrentInfo.Anesthesia = GetTextFromSpinner(R.id.anesthesiaSpinner);
- _activity.CurrentInfo.Place = GetTextFromSpinner(R.id.placeSpinner);
- _activity.CurrentInfo.Urgency = ((CheckBox) getView().findViewById(
- R.id.urgencyCheckBox)).isChecked();
-
- _activity.CurrentInfo.Notes = GetTextFromEditText(R.id.notesText);
- }
-
- private void ResetAllEntries()
- {
- GetEditText(R.id.nProcess).setText("");
- GetEditText(R.id.nSurgery).setText("");
- GetEditText(R.id.placeOverride).setText("");
- GetEditText(R.id.firstAiderOverride).setText("");
- GetEditText(R.id.anesthesiaOverride).setText("");
- GetEditText(R.id.medTypeOverride).setText("");
- GetEditText(R.id.medSubTypeOverride).setText("");
- GetEditText(R.id.medSubSubTypeOverride).setText("");
- GetEditText(R.id.pathologyOverride).setText("");
- GetEditText(R.id.interventionOverride).setText("");
- GetCheckBox(R.id.urgencyCheckBox).setChecked(false);
- GetCheckBox(R.id.overrideCheckBox).setChecked(false);
- GetEditText(R.id.notesText).setText("");
-
- // set default spinners
- GetSpinner(R.id.medTypeSpinner).setSelection(0);
- GetSpinner(R.id.firstAiderSpinner).setSelection(0);
- GetSpinner(R.id.anesthesiaSpinner).setSelection(0);
- GetSpinner(R.id.placeSpinner).setSelection(0);
-
- // hide override
- HideOverrideFields();
- }
-
- private void ApplyOverride() {
- String value;
-
- value = GetTextFromEditText(R.id.medTypeOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.Type = value;
- }
-
- value = GetTextFromEditText(R.id.medSubTypeOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.SubType = value;
- }
-
- value = GetTextFromEditText(R.id.medSubSubTypeOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.SubSubType = value;
- }
-
- value = GetTextFromEditText(R.id.pathologyOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.Pathology = value;
- }
-
- value = GetTextFromEditText(R.id.interventionOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.Intervention = value;
- }
-
- value = GetTextFromEditText(R.id.firstAiderOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.FirstAider = value;
- }
-
- value = GetTextFromEditText(R.id.anesthesiaOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.Anesthesia = value;
- }
-
- value = GetTextFromEditText(R.id.placeOverride);
- if(value.isEmpty() == false)
- {
- _activity.CurrentInfo.Place = value;
- }
- }
-
- private void HideOverrideFields() {
- GetEditText(R.id.firstAiderOverride).setVisibility(View.GONE);
- GetEditText(R.id.anesthesiaOverride).setVisibility(View.GONE);
- GetEditText(R.id.placeOverride).setVisibility(View.GONE);
- GetEditText(R.id.medTypeOverride).setVisibility(View.GONE);
- GetEditText(R.id.medSubTypeOverride).setVisibility(View.GONE);
- GetEditText(R.id.medSubSubTypeOverride).setVisibility(View.GONE);
- GetEditText(R.id.pathologyOverride).setVisibility(View.GONE);
- GetEditText(R.id.interventionOverride).setVisibility(View.GONE);
-
- ((Button) getView().findViewById(R.id.fillOverrideButton)).setVisibility(View.GONE);
- }
-
- private void ShowOverrideFields()
- {
- SetOverrideProperties(R.id.firstAiderOverride);
- SetOverrideProperties(R.id.anesthesiaOverride);
- SetOverrideProperties(R.id.placeOverride);
- SetOverrideProperties(R.id.medTypeOverride);
- SetOverrideProperties(R.id.medSubTypeOverride);
- SetOverrideProperties(R.id.medSubSubTypeOverride);
- SetOverrideProperties(R.id.pathologyOverride);
- SetOverrideProperties(R.id.interventionOverride);
-
- ((Button) getView().findViewById(R.id.fillOverrideButton)).setVisibility(View.VISIBLE);
- }
-
- private void SetOverrideProperties(int exitTextId)
- {
- int overrideColor = Color.argb(128, 51, 181, 229);
- EditText override;
- override = GetEditText(exitTextId);
- override.setVisibility(View.VISIBLE);
- override.setBackgroundColor(overrideColor);
- }
-
- private void FillOverrideFromSpinners()
- {
- GetEditText(R.id.firstAiderOverride).setText(GetTextFromSpinner(R.id.firstAiderSpinner));
- GetEditText(R.id.anesthesiaOverride).setText(GetTextFromSpinner(R.id.anesthesiaSpinner));
- GetEditText(R.id.placeOverride).setText(GetTextFromSpinner(R.id.placeSpinner));
- GetEditText(R.id.medTypeOverride).setText(GetTextFromSpinner(R.id.medTypeSpinner));
- GetEditText(R.id.medSubTypeOverride).setText(GetTextFromSpinner(R.id.medSubTypeSpinner));
- GetEditText(R.id.medSubSubTypeOverride).setText(GetTextFromSpinner(R.id.medSubSubTypeSpinner));
- GetEditText(R.id.pathologyOverride).setText(GetTextFromSpinner(R.id.pathologySpinner));
- GetEditText(R.id.interventionOverride).setText(GetTextFromSpinner(R.id.interventionSpinner));
- }
-
-}
diff --git a/src/com/example/surgerylog/ViewDataFragment.java b/src/com/example/surgerylog/ViewDataFragment.java
deleted file mode 100755
index 6adbbaa..0000000
--- a/src/com/example/surgerylog/ViewDataFragment.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package com.example.surgerylog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-import net.redroid.medlog.R;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.AdapterView.OnItemClickListener;
-
-public class ViewDataFragment extends Fragment {
- /**
- * The fragment argument representing the section number for this fragment.
- */
- public static final String ARG_SECTION_NUMBER = "section_number";
-
- private MainActivity _activity;
-
- List<String> _viewDataList = new ArrayList<String>();
-
- int _lastSelected;
-
- public ViewDataFragment() {
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- // This makes sure that the container activity has implemented
- // the callback interface. If not, it throws an exception
- try {
- _activity = (MainActivity) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnHeadlineSelectedListener");
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // get root view
- View rootView = inflater.inflate(R.layout.fragment_view_data,
- container, false);
-
- return rootView;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onActivityCreated(savedInstanceState);
-
- // get list view
- ListView listView = ((ListView) getView().findViewById(
- R.id.dataBaseListView));
-
- // set list view on click event
- listView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
-
- // hold last selected
- _lastSelected = position;
-
- // prepare the alert box
- AlertDialog.Builder _alertbox = new AlertDialog.Builder(
- _activity);
- MedLogInfo info = _activity.DataBase.get(position);
- String infoStr = "#Pro: " + info.ProcessNumber.toString()
- + "\n" + "#Cir: " + info.SurgeryNumber.toString()
- + "\n" + "Data: " + info.GetFormatedDate()
- + "\n" + "Ajud: " + info.FirstAider
- + "\n" + "Anst: " + info.Anesthesia
- + "\n" + "Locl: " + info.Place
- + "\n" + "Tipo: " + info.Type
- + "\n" + "Subt: " + info.SubType
- + "\n" + "Pato: " + info.Pathology
- + "\n" + "Intr: " + info.Intervention
- + "\n" + "Urgn: " + _activity.getString(info.Urgency == true ? R.string.yes_str : R.string.no_str)
- + "\n" + "Nota: " + info.Notes
- ;
- _alertbox.setMessage(infoStr);
- _alertbox.setNeutralButton(R.string.ok_str,
- new DialogInterface.OnClickListener() {
- // click listener on the alert box
- public void onClick(DialogInterface arg0, int arg1) {
- // nothing to do here
- }
- });
- _alertbox.setNegativeButton(R.string.delete_str,
- new DialogInterface.OnClickListener() {
- // click listener on the alert box
- public void onClick(DialogInterface arg0, int arg1) {
- // TODO delete the database entry
- _activity.DataBase.remove(_lastSelected);
- _activity.SaveMedLogDB();
- UpdateListView();
- }
- });
- _alertbox.show();
- }
- });
-
- // REFRESH BUTTON
- Button refreshBtn = (Button) getView().findViewById(R.id.refreshBtn);
- refreshBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- UpdateListView();
- }
- });
-
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- UpdateListView();
- }
-
- public void UpdateListView() {
- // get list data
- if (_activity.DataBase != null) {
-
- // remove old entries
- _viewDataList.clear();
-
- // add new entries
- for (MedLogInfo log : _activity.DataBase) {
- _viewDataList.add(log.ProcessNumber.toString());
- }
- }
-
- // create array adapter
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(getView()
- .getContext(), android.R.layout.simple_list_item_1,
- _viewDataList);
-
- // // sort list values
- // adapter.sort(new Comparator<String>() {
- // public int compare(String object1, String object2) {
- // return object1.compareTo(object2);
- // }
- // });
-
- // get list view
- ListView listView = ((ListView) getView().findViewById(
- R.id.dataBaseListView));
-
- // set adapter
- listView.setAdapter(adapter);
- }
-
-}
diff --git a/themes/dark.tres b/themes/dark.tres
new file mode 100644
index 0000000..875ee21
--- /dev/null
+++ b/themes/dark.tres
@@ -0,0 +1,126 @@
+[gd_resource type="Theme" load_steps=17 format=2]
+
+[ext_resource path="res://fonts/font_regular.tres" type="DynamicFont" id=1]
+[ext_resource path="res://fonts/font_mono_regular.tres" type="DynamicFont" id=2]
+
+[sub_resource type="StyleBoxEmpty" id=10]
+content_margin_left = 10.0
+content_margin_right = 10.0
+
+[sub_resource type="StyleBoxFlat" id=11]
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0.223529, 0.352941, 0.501961, 0.784314 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxFlat" id=2]
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0.25098, 0.25098, 0.25098, 0.870588 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxFlat" id=4]
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0.337255, 0.529412, 0.74902, 0.870588 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxEmpty" id=5]
+
+[sub_resource type="StyleBoxEmpty" id=21]
+content_margin_left = 10.0
+content_margin_right = 10.0
+content_margin_top = 15.0
+
+[sub_resource type="StyleBoxFlat" id=9]
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0, 0, 0, 0 )
+draw_center = false
+border_width_bottom = 4
+border_color = Color( 0.337255, 0.529412, 0.74902, 0.870588 )
+
+[sub_resource type="StyleBoxFlat" id=7]
+content_margin_left = 10.0
+content_margin_right = 10.0
+bg_color = Color( 0, 0, 0, 0 )
+draw_center = false
+border_width_bottom = 2
+border_color = Color( 0.329412, 0.329412, 0.329412, 1 )
+
+[sub_resource type="StyleBoxFlat" id=12]
+bg_color = Color( 0.15, 0.15, 0.15, 1 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxFlat" id=20]
+bg_color = Color( 0.223529, 0.352941, 0.501961, 0.784314 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxFlat" id=13]
+content_margin_left = 20.0
+content_margin_right = 20.0
+content_margin_top = 10.0
+content_margin_bottom = 10.0
+bg_color = Color( 0.15, 0.15, 0.15, 1 )
+corner_radius_top_left = 15
+corner_radius_top_right = 15
+corner_radius_bottom_right = 15
+corner_radius_bottom_left = 15
+
+[sub_resource type="StyleBoxFlat" id=18]
+bg_color = Color( 0.223529, 0.352941, 0.501961, 0.784314 )
+corner_radius_top_left = 10
+corner_radius_top_right = 10
+corner_radius_bottom_right = 10
+corner_radius_bottom_left = 10
+
+[sub_resource type="StyleBoxFlat" id=19]
+bg_color = Color( 0.337255, 0.529412, 0.74902, 0.870588 )
+corner_radius_top_left = 10
+corner_radius_top_right = 10
+corner_radius_bottom_right = 10
+corner_radius_bottom_left = 10
+
+[sub_resource type="StyleBoxEmpty" id=17]
+content_margin_left = 20.0
+
+[resource]
+default_font = ExtResource( 1 )
+Button/styles/disabled = SubResource( 10 )
+Button/styles/focus = SubResource( 10 )
+Button/styles/hover = SubResource( 11 )
+Button/styles/normal = SubResource( 2 )
+Button/styles/pressed = SubResource( 4 )
+ItemList/fonts/font = ExtResource( 2 )
+ItemList/styles/bg = SubResource( 5 )
+ItemList/styles/selected = SubResource( 4 )
+ItemList/styles/selected_focus = SubResource( 4 )
+Label/styles/normal = SubResource( 21 )
+LineEdit/styles/focus = SubResource( 9 )
+LineEdit/styles/normal = SubResource( 7 )
+LineEdit/styles/read_only = null
+Panel/styles/panel = SubResource( 12 )
+PopupMenu/constants/vseparation = 20
+PopupMenu/styles/hover = SubResource( 20 )
+PopupMenu/styles/panel = SubResource( 13 )
+VBoxContainer/constants/separation = 10
+VScrollBar/styles/grabber = SubResource( 18 )
+VScrollBar/styles/grabber_highlight = SubResource( 18 )
+VScrollBar/styles/grabber_pressed = SubResource( 19 )
+VScrollBar/styles/scroll = SubResource( 17 )
+VScrollBar/styles/scroll_focus = SubResource( 17 )
diff --git a/ui/date_picker/date_picker.gd b/ui/date_picker/date_picker.gd
new file mode 100644
index 0000000..e2a793f
--- /dev/null
+++ b/ui/date_picker/date_picker.gd
@@ -0,0 +1,64 @@
+extends Control
+class_name DatePicker
+
+const days_per_month: Dictionary = {
+ 1: 31,
+ 2: 28,
+ 3: 31,
+ 4: 30,
+ 5: 31,
+ 6: 30,
+ 7: 31,
+ 8: 31,
+ 9: 30,
+ 10: 31,
+ 11: 30,
+ 12: 31,
+}
+
+onready var year_picker := get_node("year") as ValuePicker
+onready var month_picker := get_node("month") as ValuePicker
+onready var day_picker := get_node("day") as ValuePicker
+
+
+func _process(delta: float):
+ var year := year_picker.value
+ var month := month_picker.value
+ var day := day_picker.value
+ var days_on_month: int = days_per_month[month]
+
+ var is_leap_year := (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
+ if is_leap_year && month == 2:
+ days_on_month = 29
+
+ if day > days_on_month:
+ day_picker.value = days_on_month
+ day_picker.max_value = days_on_month
+
+
+func get_day() -> int:
+ return day_picker.value
+
+
+func get_month() -> int:
+ return month_picker.value
+
+
+func get_year() -> int:
+ return year_picker.value
+
+
+func get_date() -> Dictionary:
+ return {
+ year = year_picker.value,
+ month = month_picker.value,
+ day = day_picker.value,
+ }
+
+
+func set_date(new_year: int, new_month: int, new_day: int):
+ year_picker.value = new_year
+ month_picker.value = new_month
+ day_picker.value = new_day
+
+
diff --git a/ui/date_picker/date_picker.tscn b/ui/date_picker/date_picker.tscn
new file mode 100644
index 0000000..e3d88c4
--- /dev/null
+++ b/ui/date_picker/date_picker.tscn
@@ -0,0 +1,189 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://ui/date_picker/value_picker.gd" type="Script" id=1]
+[ext_resource path="res://ui/date_picker/date_picker.gd" type="Script" id=2]
+
+[node name="date_picker" type="Control"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource( 2 )
+
+[node name="year" type="Control" parent="."]
+anchor_right = 0.333
+anchor_bottom = 1.0
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+min_value = 1
+max_value = 9999
+
+[node name="previous" type="Label" parent="year"]
+anchor_right = 1.0
+anchor_bottom = 0.333
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="current" type="Label" parent="year"]
+anchor_top = 0.333
+anchor_right = 1.0
+anchor_bottom = 0.666
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="input" type="LineEdit" parent="year/current"]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+focus_next = NodePath("../../../month/current/input")
+custom_constants/minimum_spaces = 0
+align = 1
+max_length = 4
+caret_blink = true
+
+[node name="next" type="Label" parent="year"]
+anchor_top = 0.666
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="month" type="Control" parent="."]
+anchor_left = 0.333
+anchor_right = 0.666
+anchor_bottom = 1.0
+script = ExtResource( 1 )
+min_value = 1
+max_value = 12
+
+[node name="previous" type="Label" parent="month"]
+anchor_right = 1.0
+anchor_bottom = 0.333
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="current" type="Label" parent="month"]
+anchor_top = 0.333
+anchor_right = 1.0
+anchor_bottom = 0.666
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="input" type="LineEdit" parent="month/current"]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+focus_next = NodePath("../../../day/current/input")
+focus_previous = NodePath("../../../year/current/input")
+custom_constants/minimum_spaces = 0
+align = 1
+max_length = 2
+caret_blink = true
+
+[node name="next" type="Label" parent="month"]
+anchor_top = 0.666
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="day" type="Control" parent="."]
+anchor_left = 0.666
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+min_value = 1
+max_value = 31
+
+[node name="previous" type="Label" parent="day"]
+anchor_right = 1.0
+anchor_bottom = 0.333
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="current" type="Label" parent="day"]
+anchor_top = 0.333
+anchor_right = 1.0
+anchor_bottom = 0.666
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="input" type="LineEdit" parent="day/current"]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+focus_previous = NodePath("../../../month/current/input")
+custom_constants/minimum_spaces = 0
+align = 1
+max_length = 2
+caret_blink = true
+
+[node name="next" type="Label" parent="day"]
+anchor_top = 0.666
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 1
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="split_upper" type="ColorRect" parent="."]
+anchor_top = 0.333
+anchor_right = 1.0
+anchor_bottom = 0.343
+color = Color( 1, 1, 1, 0.25 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="split_lower" type="ColorRect" parent="."]
+anchor_top = 0.666
+anchor_right = 1.0
+anchor_bottom = 0.676
+color = Color( 1, 1, 1, 0.25 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
diff --git a/ui/date_picker/value_picker.gd b/ui/date_picker/value_picker.gd
new file mode 100644
index 0000000..b70f1aa
--- /dev/null
+++ b/ui/date_picker/value_picker.gd
@@ -0,0 +1,132 @@
+extends Control
+
+class_name ValuePicker
+
+const VELOCITY_DECAYING_FACTOR: float = 5.5
+const BINNING_THRESHOLD: float = 0.75
+const BINNING_ADJUST_P: float = 5.0
+const DRAG_THRESHOLD_CM: float = 0.250
+
+export var min_value: int
+export var max_value: int
+
+var pointer: Dictionary
+var anchor: float
+var value: int
+var offset: float
+
+var scroll_unit_height: float
+var label_previous_base_position: float
+var label_current_base_position: float
+var label_next_base_position: float
+
+onready var input := get_node("current/input") as LineEdit
+onready var label_previous := get_node("previous") as Label
+onready var label_current := get_node("current") as Label
+onready var label_next := get_node("next") as Label
+onready var screen_dpcm := float(OS.get_screen_dpi()) / 2.54
+
+
+func _ready():
+ pointer = {
+ index = -1,
+ initial_position = Vector2.ZERO,
+ current_position = Vector2.ZERO,
+ velocity = Vector2.ZERO,
+ was_dragged = false,
+ is_active = false,
+ }
+
+ input.connect("text_entered", self, "input_text_entered")
+ input.connect("focus_entered", self, "input_focus_entered")
+ input.connect("focus_exited", self, "input_focus_exited")
+
+ scroll_unit_height = label_current.rect_size.y
+ label_previous_base_position = label_previous.rect_position.y
+ label_current_base_position = label_current.rect_position.y
+ label_next_base_position = label_next.rect_position.y
+
+ value = min_value
+ offset = 0.0
+
+
+func _process(delta: float):
+
+ if pointer.is_active:
+ var dragged_distance: float = - (pointer.current_position.y - pointer.initial_position.y)
+ offset = anchor + (dragged_distance / scroll_unit_height)
+ value = int(offset)
+ offset -= value
+ else:
+ pointer.velocity *= clamp((1.0 - VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0)
+ offset -= pointer.velocity.y * delta / scroll_unit_height
+ if abs(pointer.velocity.y) < BINNING_THRESHOLD * scroll_unit_height:
+ offset -= offset * BINNING_ADJUST_P * delta
+
+ # Using 'offset * 2.0' (equivalent to 'offset / 0.5') rounds the value based on 0.5 units.
+ var cummulative_displacement := int(offset * 2.0)
+ value = wrapi(value + cummulative_displacement, min_value, max_value + 1)
+ offset -= cummulative_displacement
+
+ label_current.text = "%d" % value
+ label_next.text = "%d" % wrapi(value + 1, min_value, max_value + 1)
+ label_previous.text = "%d" % wrapi(value - 1, min_value, max_value + 1)
+
+ var offset_position := offset * scroll_unit_height
+ label_current.rect_position.y = label_current_base_position - offset_position
+ label_previous.rect_position.y = label_previous_base_position - offset_position
+ label_next.rect_position.y = label_next_base_position - offset_position
+
+ label_previous.modulate.a = 0.5 - offset
+ label_next.modulate.a = offset + 0.5
+
+
+func _gui_input(event: InputEvent):
+
+ # @DAM A bug on GODOT-3.X makes events from non-mouse-emulated pointers (index > 0) unreliable.
+ if event is InputEventScreenTouch || event is InputEventScreenDrag:
+ if event.index != 0:
+ return
+
+ if event is InputEventScreenTouch && (pointer.is_active == false || pointer.index == event.index):
+ var touch := event as InputEventScreenTouch
+ pointer.is_active = event.pressed
+ pointer.current_position = touch.position
+ if pointer.is_active:
+ input.release_focus()
+ pointer.index = touch.index
+ pointer.initial_position = touch.position
+ anchor = value + offset
+ else:
+ if pointer.was_dragged == false: # Click detected.
+ input.grab_focus()
+ pointer.index = -1
+ pointer.was_dragged = false
+
+ if event is InputEventScreenDrag && event.index == pointer.index:
+ var drag := event as InputEventScreenDrag
+ pointer.current_position = drag.position
+ pointer.velocity = drag.speed
+ if pointer.current_position.distance_to(pointer.initial_position) / screen_dpcm > DRAG_THRESHOLD_CM:
+ pointer.was_dragged = true
+
+
+func input_text_entered(new_text: String):
+ input.release_focus()
+
+
+func input_focus_entered():
+ pointer.velocity = Vector2.ZERO # Avoid changing to other value once entering input.
+ input.text = "%d" % value
+ input.visible = true
+ input.select_all()
+ label_current.self_modulate.a = 0.0
+
+
+func input_focus_exited():
+ if input.text.is_valid_integer():
+ value = wrapi(int(input.text), min_value, max_value + 1)
+ input.visible = false
+ label_current.self_modulate.a = 1.0
+
+
diff --git a/ui/dialog/dialog.gd b/ui/dialog/dialog.gd
new file mode 100644
index 0000000..89fee5c
--- /dev/null
+++ b/ui/dialog/dialog.gd
@@ -0,0 +1,79 @@
+extends Control
+class_name Dialog
+
+signal answered # (accepted: bool)
+signal accepted # ()
+signal rejected # ()
+
+export var clear_signals_on_hide := true
+
+var message : Label
+var accept_button : Button
+var reject_button : Button
+
+
+func _init():
+ self.anchor_right = 1.0
+ self.anchor_bottom = 1.0
+ self.rect_clip_content = true
+ self.connect("hide", self, "_clear_signals")
+
+ reject_button = Button.new()
+ reject_button.anchor_top = 1.0
+ reject_button.anchor_left = 0.0
+ reject_button.anchor_right = 0.5
+ reject_button.margin_right = -5.0
+ reject_button.rect_min_size.y = 62.0
+ reject_button.grow_vertical = Control.GROW_DIRECTION_BEGIN
+ reject_button.name = "reject"
+ reject_button.connect("pressed", self, "_signal_rejected")
+ add_child(reject_button)
+
+ accept_button = Button.new()
+ accept_button.anchor_top = 1.0
+ accept_button.anchor_left = 0.5
+ accept_button.anchor_right = 1.0
+ accept_button.margin_left = 5.0
+ accept_button.rect_min_size.y = 62.0
+ accept_button.grow_vertical = Control.GROW_DIRECTION_BEGIN
+ accept_button.name = "accept"
+ accept_button.connect("pressed", self, "_signal_accepted")
+ add_child(accept_button)
+
+ message = Label.new()
+ message.autowrap = true
+ message.align = Label.ALIGN_CENTER
+ message.anchor_right = 1.0
+ message.anchor_bottom = 1.0
+ add_child(message)
+
+
+func _clear_signals():
+ if clear_signals_on_hide == false:
+ return
+
+ for signal_name in ["answered", "accepted", "rejected"]:
+ for it in get_signal_connection_list(signal_name):
+ disconnect(it.signal, it.target, it.method)
+
+
+func _signal_rejected():
+ emit_signal("rejected")
+ emit_signal("answered", false)
+ hide()
+
+
+func _signal_accepted():
+ emit_signal("accepted")
+ emit_signal("answered", true)
+ hide()
+
+
+func setup(message: String, accept_label: String = "Accept", reject_label: String = "Reject"):
+ self.message.text = message
+ accept_button.visible = accept_label != ""
+ accept_button.text = accept_label
+ reject_button.visible = reject_label != ""
+ reject_button.text = reject_label
+
+
diff --git a/ui/modal_popup/modal_popup.gd b/ui/modal_popup/modal_popup.gd
new file mode 100644
index 0000000..95fc2c3
--- /dev/null
+++ b/ui/modal_popup/modal_popup.gd
@@ -0,0 +1,76 @@
+extends Control
+class_name ModalPopup
+
+signal dismissed # ()
+
+export var clear_signals_on_hide := true
+
+var control : Control
+var control_parent : Node
+
+onready var title := get_node("title") as Label
+onready var background := get_node("background") as Panel
+onready var dismiss_button := get_node("dismiss") as Button
+
+
+func _init():
+ self.anchor_right = 1.0
+ self.anchor_bottom = 1.0
+ self.rect_clip_content = true
+ self.connect("hide", self, "_clear_signals")
+
+
+func _ready():
+ dismiss_button.connect("pressed", self, "dismiss")
+
+
+func _clear_signals():
+ if clear_signals_on_hide == false:
+ return
+
+ for signal_name in ["dismissed"]:
+ for it in get_signal_connection_list(signal_name):
+ disconnect(it.signal, it.target, it.method)
+
+
+func dismiss():
+ emit_signal("dismissed")
+ close_popup()
+
+
+func open_popup(title: String, item: Control):
+ if visible == true:
+ return
+
+ self.title.text = title
+
+ control = item
+ control.connect("hide", self, "close_popup")
+ control_parent = control.get_parent()
+ control_parent.remove_child(control)
+ self.add_child(control)
+
+ control.anchor_left = background.anchor_left
+ control.anchor_top = background.anchor_top
+ control.anchor_right = background.anchor_right
+ control.anchor_bottom = background.anchor_bottom
+ control.margin_left = 20
+ control.margin_top = 20
+ control.margin_right = -20
+ control.margin_bottom = -20
+
+ self.show()
+ control.show()
+
+
+func close_popup():
+ if visible == false:
+ return
+
+ control.disconnect("hide", self, "close_popup")
+ control.hide()
+ self.hide()
+ remove_child(control)
+ control_parent.add_child(control)
+ control_parent = null
+
diff --git a/ui/modal_popup/modal_popup.tscn b/ui/modal_popup/modal_popup.tscn
new file mode 100644
index 0000000..3814bef
--- /dev/null
+++ b/ui/modal_popup/modal_popup.tscn
@@ -0,0 +1,40 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://ui/modal_popup/modal_popup.gd" type="Script" id=1]
+[ext_resource path="res://fonts/font_icons.tres" type="DynamicFont" id=2]
+
+[node name="popup" type="ColorRect"]
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+color = Color( 0, 0, 0, 0.870588 )
+script = ExtResource( 1 )
+
+[node name="background" type="Panel" parent="."]
+anchor_left = 0.05
+anchor_top = 0.1
+anchor_right = 0.95
+anchor_bottom = 0.975
+
+[node name="title" type="Label" parent="."]
+anchor_left = 0.124
+anchor_top = 0.025
+anchor_right = 0.95
+anchor_bottom = 0.1
+margin_left = 0.0799866
+margin_right = -80.0
+margin_bottom = -20.0
+align = 1
+valign = 1
+autowrap = true
+
+[node name="dismiss" type="Button" parent="."]
+anchor_left = 0.05
+anchor_top = 0.025
+anchor_right = 0.124
+anchor_bottom = 0.1
+margin_right = 0.0799866
+margin_bottom = -20.0
+custom_fonts/font = ExtResource( 2 )
+text = ""
+flat = true
diff --git a/ui/option_set/option_set.gd b/ui/option_set/option_set.gd
new file mode 100644
index 0000000..25ca0ff
--- /dev/null
+++ b/ui/option_set/option_set.gd
@@ -0,0 +1,44 @@
+extends Control
+class_name OptionSet
+
+export var placeholder: String
+
+var text: String setget set_text, get_text
+
+func set_text(var value: String):
+ input.text = value
+
+func get_text() -> String:
+ return input.text
+
+var selected_idx: int
+
+onready var input := get_node("input") as LineEdit
+onready var button := get_node("button") as Button
+onready var popup := get_node("/root/main/popup") as ModalPopup
+onready var options := get_node("/root/main/option_set_list") as OptionSetList
+
+
+func _ready():
+ assert(popup != null, "OptionSet failed to get 'popup' node.")
+ input.placeholder_text = placeholder
+ input.connect("focus_entered", input, "set", ["caret_position", input.max_length])
+ input.connect("focus_exited", input, "deselect")
+
+
+func show_options(options_array: Array):
+ options.clear_items()
+ options.add_items(options_array)
+ options.select(options_array.find(input.text))
+ options.connect("selection_changed", self, "popup_result")
+ popup.open_popup(input.placeholder_text, options)
+
+
+func popup_result(index: int, text: String):
+ if index != -1:
+ selected_idx = index
+ input.text = text
+ input.caret_position = input.max_length
+ popup.close_popup()
+
+
diff --git a/ui/option_set/option_set.tscn b/ui/option_set/option_set.tscn
new file mode 100644
index 0000000..b971429
--- /dev/null
+++ b/ui/option_set/option_set.tscn
@@ -0,0 +1,34 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://ui/option_set/option_set.gd" type="Script" id=1]
+[ext_resource path="res://fonts/font_icons.tres" type="DynamicFont" id=2]
+
+[node name="option_set" type="Control"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource( 1 )
+
+[node name="input" type="LineEdit" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_right = -100.0
+size_flags_horizontal = 3
+max_length = 4096
+placeholder_text = "placeholder"
+caret_blink = true
+caret_blink_speed = 0.5
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="button" type="Button" parent="."]
+anchor_left = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = -100.0
+grow_horizontal = 0
+custom_fonts/font = ExtResource( 2 )
+text = ""
+__meta__ = {
+"_edit_use_anchors_": false
+}
diff --git a/ui/option_set/option_set_list.gd b/ui/option_set/option_set_list.gd
new file mode 100644
index 0000000..fda2c04
--- /dev/null
+++ b/ui/option_set/option_set_list.gd
@@ -0,0 +1,294 @@
+extends Control
+class_name OptionSetList
+
+const POINTER_VELOCITY_DECAYING_FACTOR: float = PI
+const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25
+const EXACT_SELECTION: bool = false
+
+signal selection_changed # (idx: int, text: String)
+
+export var clear_signals_on_hide := true
+export var autowrap := true
+export var separation := 25
+
+var v_scroll_bar : VScrollBar
+var sensor : PointerInputSensor
+var is_pointer_dragging := false
+var pointer_drag_velocity := 0.0
+var when_last_dragged := 0
+
+var labels : Control
+var labels_end_positions : Array
+var labels_sizes : Array
+
+var items : Array
+var selected_idx := -1
+var selected_item := ""
+var is_dirty := true
+
+var normal_style : StyleBoxFlat
+var selected_style : StyleBoxFlat
+var border_size := 5
+
+onready var font : Font = get_font("font")
+onready var screen_dpcm : float = float(OS.get_screen_dpi()) / 2.54
+
+func _init():
+ self.anchor_right = 1.0
+ self.anchor_bottom = 1.0
+ self.rect_clip_content = true
+ self.connect("hide", self, "_clear_signals")
+
+ labels = Control.new()
+ labels.anchor_right = 1.0
+ labels.anchor_bottom = 1.0
+ labels.mouse_filter = Control.MOUSE_FILTER_IGNORE
+ labels.name = "labels"
+ add_child(labels)
+
+ v_scroll_bar = VScrollBar.new()
+ v_scroll_bar.anchor_left = 1.0
+ v_scroll_bar.anchor_bottom = 1.0
+ v_scroll_bar.grow_horizontal = Control.GROW_DIRECTION_BEGIN
+ v_scroll_bar.name = "v_scroll_bar"
+ add_child(v_scroll_bar)
+
+ sensor = PointerInputSensor.new()
+ sensor.anchor_right = 1.0
+ sensor.anchor_bottom = 1.0
+ sensor.name = "sensor"
+ add_child(sensor)
+
+ connect("resized", self, "mark_as_dirty")
+
+ sensor.connect("on_press", self, "pointer_input_on_press_handler")
+ sensor.connect("on_drag", self, "pointer_input_on_drag_handler")
+ sensor.connect("on_end_drag", self, "pointer_input_on_end_drag_handler")
+ sensor.connect("on_click", self, "pointer_input_on_click_handler")
+ sensor.connect("on_scroll", self, "pointer_input_on_scroll_handler")
+
+
+func _ready():
+ var button_style := get_stylebox("pressed", "Button")
+ var label_style := get_stylebox("normal", "Label")
+
+ normal_style = StyleBoxFlat.new()
+ normal_style.border_width_bottom = border_size
+ normal_style.border_color = Color.white * 0.333
+ normal_style.bg_color = Color.transparent
+ normal_style.content_margin_left = label_style.content_margin_left
+ normal_style.content_margin_top = label_style.content_margin_top
+ normal_style.content_margin_right = label_style.content_margin_right
+
+ var bg_color := button_style.bg_color as Color
+ var corner_radius := button_style.corner_radius_top_right as int
+ selected_style = StyleBoxFlat.new()
+ selected_style.bg_color = bg_color
+ selected_style.corner_radius_top_left = corner_radius
+ selected_style.corner_radius_top_right = corner_radius
+ selected_style.corner_radius_bottom_right = corner_radius
+ selected_style.corner_radius_bottom_left = corner_radius
+ selected_style.content_margin_left = label_style.content_margin_left
+ selected_style.content_margin_top = label_style.content_margin_top
+ selected_style.content_margin_right = label_style.content_margin_right
+
+
+func mark_as_dirty():
+ is_dirty = true
+
+
+func add_item(text: String):
+ items.append(text)
+ is_dirty = true
+
+
+func add_items(texts: Array):
+ items.append_array(texts)
+ is_dirty = true
+
+
+func set_item(index: int, text: String):
+ items[index] = text
+ is_dirty = true
+
+
+func set_items(indices: Array, texts: Array):
+ for idx in indices.size():
+ items[indices[idx]] = texts[idx]
+ is_dirty = true
+
+
+func remove_item(index: int):
+ items.remove(index)
+ is_dirty = true
+
+
+func remove_items(indices: Array):
+ indices.sort()
+ var idx := indices.size()-1
+ while idx >= 0:
+ items.remove(indices[idx])
+ idx -= 1
+ is_dirty = true
+
+
+func clear_items():
+ items.clear()
+ selected_idx = -1
+ selected_item = ""
+ v_scroll_bar.value = 0.0
+ is_dirty = true
+
+
+func build_labels():
+
+ var num_of_labels := int(min(
+ items.size(),
+ ceil(labels.rect_size.y / (font.get_height() + border_size)) + 1
+ ))
+
+ var delta := num_of_labels - labels.get_child_count()
+
+ while delta > 0:
+ var label := Label.new()
+ label.autowrap = autowrap
+ label.anchor_right = 1.0
+ label.valign = Label.VALIGN_TOP
+ labels.add_child(label)
+ delta -= 1
+
+ while delta < 0:
+ var label := labels.get_child(0) as Label
+ labels.remove_child(label)
+ label.free()
+ delta += 1
+
+ labels_end_positions.clear()
+ labels_sizes.clear()
+
+ if num_of_labels == 0:
+ return
+
+ var position := 0.0
+ var proto_label := labels.get_child(0) as Label
+ var line_spacing := proto_label.get_constant("line_spacing")
+ var line_height := proto_label.get_line_height()
+ for it in items:
+ proto_label.text = it
+ var line_count := proto_label.get_line_count()
+ var height := line_count * line_height + (line_count - 1) * line_spacing + border_size + separation
+
+ position += height
+ labels_end_positions.append(position)
+ labels_sizes.append(height)
+
+
+func _process(delta):
+
+ if is_dirty:
+ build_labels()
+ is_dirty = false
+
+ var viewable_ratio := 1.0
+ var viewable_height := labels.rect_size.y
+ if items.size() > 0:
+ viewable_ratio = viewable_height / float(labels_end_positions.back())
+
+ v_scroll_bar.min_value = 0
+ v_scroll_bar.max_value = viewable_height / viewable_ratio
+ v_scroll_bar.visible = viewable_ratio < 1.0
+ v_scroll_bar.page = viewable_height
+
+ var labels_offset := v_scroll_bar.value
+ var idx_offset := labels_end_positions.bsearch(labels_offset)
+
+ var idx := idx_offset
+ for label in labels.get_children():
+
+ if idx >= items.size():
+ label.visible = false
+ continue
+
+ label.visible = true
+ label.text = items[idx]
+ label.rect_size.y = labels_sizes[idx]
+ label.rect_position.y = labels_end_positions[idx] - labels_sizes[idx] - labels_offset
+
+ if idx == selected_idx:
+ label.add_stylebox_override("normal", selected_style)
+ else:
+ label.add_stylebox_override("normal", normal_style)
+
+ idx += 1
+
+ var right_margin = - v_scroll_bar.rect_size.x if v_scroll_bar.visible else 0.0
+ labels.margin_right = right_margin
+ sensor.margin_right = right_margin
+
+ # Apply drag movement inertia.
+ if is_pointer_dragging == false && abs(pointer_drag_velocity) > 0.5:
+ pointer_drag_velocity *= clamp((1.0 - POINTER_VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0)
+ v_scroll_bar.value -= pointer_drag_velocity * delta
+
+
+func get_item_at_position(mouse_position: Vector2) -> int:
+ var labels_offset := v_scroll_bar.value
+ var position := mouse_position.y + labels_offset
+ var item_idx := labels_end_positions.bsearch(position)
+
+ if item_idx == items.size():
+ item_idx = -1
+ return item_idx
+
+
+func select(index: int):
+ selected_idx = index
+ selected_item = items[selected_idx] if selected_idx >= 0 && selected_idx < items.size() else ""
+ emit_signal("selection_changed", selected_idx, selected_item)
+
+
+func unselect():
+ select(-1)
+
+
+func pointer_input_on_press_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ grab_focus()
+
+
+func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ var reported_velocity_abs := abs(pointer.velocity.y)
+ var relative_velocity := pointer.relative_position.y * Engine.get_frames_per_second()
+ var relative_velocity_abs := abs(relative_velocity)
+ pointer_drag_velocity = pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity
+ v_scroll_bar.value -= pointer.relative_position.y
+ when_last_dragged = OS.get_ticks_msec()
+
+
+func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = false
+ if OS.get_ticks_msec() - when_last_dragged > 20:
+ pointer_drag_velocity = 0.0
+ else:
+ pointer_drag_velocity *= POINTER_VELOCITY_BOOST_FACTOR
+
+
+func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData):
+ var item_idx := get_item_at_position(pointer.current_position - rect_global_position)
+ select(item_idx)
+
+
+func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputData):
+ pointer_drag_velocity -= pointer.scroll * POINTER_VELOCITY_BOOST_FACTOR * screen_dpcm
+
+
+func _clear_signals():
+ if clear_signals_on_hide == false:
+ return
+
+ for signal_name in ["selection_changed"]:
+ for it in get_signal_connection_list(signal_name):
+ disconnect(it.signal, it.target, it.method)
+
+
diff --git a/ui/option_set/option_set_list.tscn b/ui/option_set/option_set_list.tscn
new file mode 100644
index 0000000..82473a5
--- /dev/null
+++ b/ui/option_set/option_set_list.tscn
@@ -0,0 +1,9 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://ui/option_set/option_set_list.gd" type="Script" id=1]
+
+[node name="option_set_list" type="Control"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+rect_clip_content = true
+script = ExtResource( 1 )
diff --git a/ui/pointer_input_sensor.gd b/ui/pointer_input_sensor.gd
new file mode 100644
index 0000000..f99fc17
--- /dev/null
+++ b/ui/pointer_input_sensor.gd
@@ -0,0 +1,117 @@
+extends Control
+class_name PointerInputSensor
+
+# All on_ACTION signals have a single argument of type PointerInputData.
+signal on_press
+signal on_release
+#signal on_release_outside
+signal on_click
+signal on_enter
+signal on_exit
+#signal on_exit_app_window
+signal on_begin_drag
+signal on_drag
+signal on_end_drag
+signal on_scroll
+
+enum PointerInputAction {
+ UNDEFINED,
+ ON_PRESS,
+ ON_RELEASE,
+# ON_RELEASE_OUTSIDE,
+ ON_CLICK,
+ ON_ENTER,
+ ON_EXIT,
+# ON_EXIT_APP_WINDOW,
+ ON_BEGIN_DRAG,
+ ON_DRAG,
+ ON_END_DRAG,
+ ON_SCROLL,
+}
+
+class PointerInputData:
+ var target: PointerInputSensor
+ var event: InputEvent
+ var index := -1
+ var initial_position := Vector2.ZERO
+ var current_position := Vector2.ZERO
+ var relative_position := Vector2.ZERO
+ var velocity := Vector2.ZERO
+ var was_dragged := false
+ var is_pressed := false
+ var scroll := 0.0
+ var action: int = PointerInputAction.UNDEFINED
+
+
+export var drag_threshold_cm: float = 0.250
+
+var pointer: PointerInputData
+
+onready var screen_dpcm := float(OS.get_screen_dpi()) / 2.54
+
+
+func _ready():
+ pointer = PointerInputData.new()
+ pointer.target = self
+ connect("mouse_entered", self, "_on_enter_exit", [true])
+ connect("mouse_exited", self, "_on_enter_exit", [false])
+
+
+func _on_enter_exit(is_inside: bool):
+ pointer.action = PointerInputAction.ON_ENTER if is_inside else PointerInputAction.ON_EXIT
+ emit_signal("on_enter" if is_inside else "on_exit", pointer)
+
+
+func _gui_input(event: InputEvent):
+
+ # @DAM A bug on GODOT-3.X makes events from non-mouse-emulated pointers (index > 0) unreliable.
+ if event is InputEventScreenTouch || event is InputEventScreenDrag:
+ if event.index != 0:
+ return
+
+ pointer.event = event
+ pointer.action = PointerInputAction.UNDEFINED
+ pointer.velocity = Vector2.ZERO
+ pointer.relative_position = Vector2.ZERO
+ pointer.scroll = 0.0
+ pointer.current_position = get_global_mouse_position()
+
+ if event is InputEventScreenTouch && (pointer.is_pressed == false || pointer.index == event.index):
+ var touch := event as InputEventScreenTouch
+ pointer.is_pressed = event.pressed
+
+ if pointer.is_pressed:
+ pointer.index = touch.index
+ pointer.initial_position = pointer.current_position
+ pointer.action = PointerInputAction.ON_PRESS
+ emit_signal("on_press", pointer)
+ else:
+ pointer.action = PointerInputAction.ON_RELEASE
+ emit_signal("on_release", pointer)
+ if pointer.was_dragged == false:
+ pointer.action = PointerInputAction.ON_CLICK
+ emit_signal("on_click", pointer)
+ else:
+ pointer.action = PointerInputAction.ON_END_DRAG
+ emit_signal("on_end_drag", pointer)
+ pointer.index = -1
+ pointer.was_dragged = false
+
+ if event is InputEventScreenDrag && event.index == pointer.index:
+ var drag := event as InputEventScreenDrag
+ pointer.velocity = drag.speed
+ pointer.relative_position = drag.relative
+ if pointer.was_dragged == false && pointer.current_position.distance_to(pointer.initial_position) > drag_threshold_cm * screen_dpcm:
+ pointer.was_dragged = true
+ pointer.action = PointerInputAction.ON_BEGIN_DRAG
+ emit_signal("on_begin_drag", pointer)
+ pointer.action = PointerInputAction.ON_DRAG
+ emit_signal("on_drag", pointer)
+
+ if event is InputEventMouseButton && event.is_pressed():
+ if event.button_index == BUTTON_WHEEL_UP || event.button_index == BUTTON_WHEEL_DOWN:
+ pointer.scroll = -1.0 if event.button_index == BUTTON_WHEEL_UP else 1.0
+ pointer.action = PointerInputAction.ON_SCROLL
+ emit_signal("on_scroll", pointer)
+
+
diff --git a/ui/touch_item_list/touch_item_list.gd b/ui/touch_item_list/touch_item_list.gd
new file mode 100644
index 0000000..6794138
--- /dev/null
+++ b/ui/touch_item_list/touch_item_list.gd
@@ -0,0 +1,68 @@
+extends ItemList
+class_name TouchItemList
+
+const POINTER_VELOCITY_DECAYING_FACTOR: float = PI
+const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25
+const EXACT_SELECTION: bool = true
+
+var is_pointer_dragging := false
+var pointer_drag_velocity := 0.0
+var when_last_dragged := 0
+
+onready var sensor := get_node("sensor") as PointerInputSensor
+onready var v_scroll_bar := get_v_scroll() as ScrollBar
+
+
+func _ready():
+ sensor.connect("on_press", self, "pointer_input_on_press_handler")
+ sensor.connect("on_drag", self, "pointer_input_on_drag_handler")
+ sensor.connect("on_end_drag", self, "pointer_input_on_end_drag_handler")
+ sensor.connect("on_click", self, "pointer_input_on_click_handler")
+ sensor.connect("on_scroll", self, "pointer_input_on_scroll_handler")
+
+
+func _process(delta: float):
+ # Apply drag movement inertia.
+ if is_pointer_dragging == false && abs(pointer_drag_velocity) > 0.5:
+ pointer_drag_velocity *= clamp((1.0 - POINTER_VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0)
+ v_scroll_bar.value -= pointer_drag_velocity * delta
+
+
+func pointer_input_on_press_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ grab_focus()
+
+
+func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ var reported_velocity_abs := abs(pointer.velocity.y)
+ var relative_velocity := pointer.relative_position.y * Engine.get_frames_per_second()
+ var relative_velocity_abs := abs(relative_velocity)
+ pointer_drag_velocity = pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity
+ v_scroll_bar.value -= pointer.relative_position.y
+ when_last_dragged = OS.get_ticks_msec()
+
+
+func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = false
+ if OS.get_ticks_msec() - when_last_dragged > 20:
+ pointer_drag_velocity = 0.0
+ else:
+ pointer_drag_velocity *= POINTER_VELOCITY_BOOST_FACTOR
+
+
+func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData):
+ var selected_idx := get_item_at_position(pointer.current_position - rect_global_position, EXACT_SELECTION)
+ if selected_idx >= 0:
+ select(selected_idx)
+ emit_signal("item_selected", selected_idx)
+ else:
+ unselect_all()
+ emit_signal("nothing_selected")
+
+
+func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputData):
+ var target := self
+ target._gui_input(pointer.event)
+
+
diff --git a/ui/touch_item_list/touch_item_list.tscn b/ui/touch_item_list/touch_item_list.tscn
new file mode 100644
index 0000000..38f9a7e
--- /dev/null
+++ b/ui/touch_item_list/touch_item_list.tscn
@@ -0,0 +1,19 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://ui/pointer_input_sensor.gd" type="Script" id=1]
+[ext_resource path="res://ui/touch_item_list/touch_item_list.gd" type="Script" id=2]
+
+[node name="item_list" type="ItemList"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 2
+script = ExtResource( 2 )
+
+[node name="sensor" type="Control" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_right = -8.0
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
diff --git a/ui/touch_vertical_container/touch_vertical_container.gd b/ui/touch_vertical_container/touch_vertical_container.gd
new file mode 100644
index 0000000..8f87338
--- /dev/null
+++ b/ui/touch_vertical_container/touch_vertical_container.gd
@@ -0,0 +1,118 @@
+extends ScrollContainer
+class_name TouchVerticalContainer
+
+const POINTER_VELOCITY_DECAYING_FACTOR: float = PI
+const POINTER_VELOCITY_BOOST_FACTOR: float = 1.25
+
+var is_pointer_dragging := false
+var pointer_drag_velocity := 0.0
+var when_last_dragged := 0
+var exclude_controls := []
+
+onready var controls = get_node("controls")
+
+
+func _ready():
+ assert(controls != null, "TouchVerticalContainer failed to get 'controls' node.")
+
+ var item_list_separation := float(controls.get_constant("separation"))
+
+ for it in controls.get_children():
+ it = it as Control
+ if exclude_controls.has(it.name):
+ continue
+
+ var sensor = PointerInputSensor.new()
+ it.add_child(sensor)
+
+ sensor.name = "sensor"
+ sensor.anchor_right = 1.0
+ sensor.anchor_bottom = 1.0
+ sensor.margin_top = - item_list_separation / 2.0
+ sensor.margin_bottom = item_list_separation / 2.0
+
+ sensor.connect("on_press", self, "pointer_input_on_press_handler")
+ sensor.connect("on_drag", self, "pointer_input_on_drag_handler")
+ sensor.connect("on_end_drag", self, "pointer_input_on_end_drag_handler")
+ sensor.connect("on_click", self, "pointer_input_on_click_handler")
+ sensor.connect("on_scroll", self, "pointer_input_on_scroll_handler")
+
+
+func _process(delta: float):
+ # Apply drag movement inertia.
+ if is_pointer_dragging == false && abs(pointer_drag_velocity) > 0.5:
+ pointer_drag_velocity *= clamp((1.0 - POINTER_VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0)
+ self.scroll_vertical -= pointer_drag_velocity * delta
+
+
+func pointer_input_on_press_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ grab_focus()
+
+
+func pointer_input_on_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = true
+ var reported_velocity_abs := abs(pointer.velocity.y)
+ var relative_velocity := pointer.relative_position.y * Engine.get_frames_per_second()
+ var relative_velocity_abs := abs(relative_velocity)
+ pointer_drag_velocity = pointer.velocity.y if reported_velocity_abs > relative_velocity_abs else relative_velocity
+ self.scroll_vertical -= pointer.relative_position.y
+ when_last_dragged = OS.get_ticks_msec()
+
+
+func pointer_input_on_end_drag_handler(pointer: PointerInputSensor.PointerInputData):
+ is_pointer_dragging = false
+ if OS.get_ticks_msec() - when_last_dragged > 20:
+ pointer_drag_velocity = 0.0
+ else:
+ pointer_drag_velocity *= POINTER_VELOCITY_BOOST_FACTOR
+
+
+func pointer_input_on_click_handler(pointer: PointerInputSensor.PointerInputData):
+ # Get last leaf node.
+ var root := pointer.target.get_parent() as Control
+ var leaf := root as Node
+ while leaf.get_child_count() > 0:
+ leaf = leaf.get_child(leaf.get_child_count() - 1)
+
+ # Navigate backwards from leaf to root until we find a node accepting the input.
+ var tried_leaf_as_root := false
+ while leaf != root || tried_leaf_as_root == false:
+ tried_leaf_as_root = leaf == root # Allow a final iteration cycle when leaf reaches root.
+ var node := leaf
+
+ if node is PointerInputSensor \
+ || node is Control == false \
+ || node.mouse_filter == MOUSE_FILTER_IGNORE \
+ || node.visible == false \
+ || node.get_global_rect().has_point(pointer.current_position) == false:
+ # Get next node to be processed.
+ var leaf_index_in_parent := leaf.get_position_in_parent()
+ var parent := leaf.get_parent()
+ if leaf_index_in_parent == 0:
+ leaf = parent
+ else:
+ leaf = parent.get_child(leaf_index_in_parent - 1)
+ # Drill down into the new tree branch.
+ while leaf.get_child_count() > 0:
+ leaf = leaf.get_child(leaf.get_child_count() - 1)
+ continue
+
+ var control: Control = node
+ if control is CheckBox || control is CheckButton || (control is Button && control.toggle_mode == true):
+ control.pressed = !control.pressed
+
+ control.grab_focus()
+ control.emit_signal("button_down")
+ control.emit_signal("pressed")
+ control.emit_signal("button_up")
+ control.connect("focus_exited", pointer.target, "set_visible", [true], CONNECT_ONESHOT)
+ pointer.target.visible = false
+ break
+
+
+func pointer_input_on_scroll_handler(pointer: PointerInputSensor.PointerInputData):
+ var target := self
+ target._gui_input(pointer.event)
+
+
diff --git a/ui/touch_vertical_container/touch_vertical_container.tscn b/ui/touch_vertical_container/touch_vertical_container.tscn
new file mode 100644
index 0000000..e741e8a
--- /dev/null
+++ b/ui/touch_vertical_container/touch_vertical_container.tscn
@@ -0,0 +1,19 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://ui/touch_vertical_container/touch_vertical_container.gd" type="Script" id=1]
+
+[node name="touch_vertical_container" type="ScrollContainer"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 2
+scroll_horizontal_enabled = false
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="controls" type="VBoxContainer" parent="."]
+margin_right = 1080.0
+margin_bottom = 1920.0
+size_flags_horizontal = 3
+size_flags_vertical = 3