diff options
| author | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2022-04-18 09:06:19 +0000 |
| commit | 79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch) | |
| tree | 6e90c6601893895d7640f478f0cad402cc7590b4 | |
| parent | 697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff) | |
| parent | ee31a9a3d387121030a5f4503adeac5816d7726f (diff) | |
| download | surgery-log-1.0.tar.zst surgery-log-1.0.zip | |
Merge godot branch.v1.0
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 Binary files differnew file mode 100644 index 0000000..7b937fa --- /dev/null +++ b/debug.keystore 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 Binary files differnew file mode 100644 index 0000000..0574897 --- /dev/null +++ b/fonts/B612-Regular.ttf diff --git a/fonts/B612Mono-Regular.ttf b/fonts/B612Mono-Regular.ttf Binary files differnew file mode 100644 index 0000000..ec09c98 --- /dev/null +++ b/fonts/B612Mono-Regular.ttf diff --git a/fonts/entypo-fontello.ttf b/fonts/entypo-fontello.ttf Binary files differnew file mode 100644 index 0000000..345bd55 --- /dev/null +++ b/fonts/entypo-fontello.ttf 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 Binary files differdeleted file mode 100755 index e4d20ec..0000000 --- a/ic_launcher-web.png +++ /dev/null diff --git a/icons/icon.png b/icons/icon.png Binary files differnew file mode 100755 index 0000000..95c7db3 --- /dev/null +++ b/icons/icon.png 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 Binary files differnew file mode 100644 index 0000000..57986fb --- /dev/null +++ b/icons/icon.xcf diff --git a/icons/icon_background.png b/icons/icon_background.png Binary files differnew file mode 100644 index 0000000..bbb555d --- /dev/null +++ b/icons/icon_background.png 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 Binary files differnew file mode 100644 index 0000000..919f2ff --- /dev/null +++ b/icons/icon_foreground.png 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 Binary files differdeleted file mode 100755 index 96644ed..0000000 --- a/libs/android-support-v4.jar +++ /dev/null diff --git a/libs/javacsv.jar b/libs/javacsv.jar Binary files differdeleted file mode 100755 index ceb59eb..0000000 --- a/libs/javacsv.jar +++ /dev/null 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 = {} + + @@ -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 Binary files differdeleted file mode 100755 index 9ef6cfb..0000000 --- a/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100755 index 330d0a4..0000000 --- a/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100755 index e6341a6..0000000 --- a/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100755 index 590a40f..0000000 --- a/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null 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 |
